- Added classes and macros for TPC PID calibration
authorbhess <bhess@cern.ch>
Thu, 12 Jun 2014 06:40:04 +0000 (08:40 +0200)
committershahoian <ruben.shahoyan@cern.ch>
Mon, 16 Jun 2014 21:49:47 +0000 (23:49 +0200)
23 files changed:
PWGPP/TPC/AliTPCPIDBase.cxx [new file with mode: 0644]
PWGPP/TPC/AliTPCPIDBase.h [new file with mode: 0644]
PWGPP/TPC/AliTPCPIDEtaQA.cxx [new file with mode: 0644]
PWGPP/TPC/AliTPCPIDEtaQA.h [new file with mode: 0644]
PWGPP/TPC/AliTPCPIDEtaTree.cxx [new file with mode: 0644]
PWGPP/TPC/AliTPCPIDEtaTree.h [new file with mode: 0644]
PWGPP/TPC/AliTPCcalibResidualPID.cxx [new file with mode: 0644]
PWGPP/TPC/AliTPCcalibResidualPID.h [new file with mode: 0644]
PWGPP/TPC/macros/AddTaskTPCPIDEtaQA.C [new file with mode: 0644]
PWGPP/TPC/macros/AddTaskTPCPIDEtaTree.C [new file with mode: 0644]
PWGPP/TPC/macros/AddTaskTPCcalibResidualPID.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/ProgressBar.h [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/THnSparseDefinitions.h [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/addMapToFile.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/checkPullTree.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/checkShapeEta.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/checkShapeEtaTree.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/checkShapeEtaTreePions.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/compareSplines.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/correctShapeEtaTree.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/createMapFile.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/extractMultiplicityDependence.C [new file with mode: 0644]
PWGPP/TPC/macros/PIDCalib/splitPbPbTreeMultiplicity.C [new file with mode: 0644]

diff --git a/PWGPP/TPC/AliTPCPIDBase.cxx b/PWGPP/TPC/AliTPCPIDBase.cxx
new file mode 100644 (file)
index 0000000..4f6f53f
--- /dev/null
@@ -0,0 +1,521 @@
+#include "TChain.h"
+#include "TF1.h"
+#include "TRandom3.h"
+
+#include "AliMCParticle.h"
+
+#include "AliAnalysisTask.h"
+#include "AliAnalysisManager.h"
+
+#include "AliVEvent.h"
+#include "AliESDEvent.h"
+#include "AliAODEvent.h"
+#include "AliMCEvent.h"
+#include "AliESDInputHandler.h"
+#include "AliInputEventHandler.h"
+#include "AliVTrack.h"
+#include "AliExternalTrackParam.h"
+#include "AliVVertex.h"
+#include "AliAnalysisFilter.h"
+#include "AliPID.h"
+#include "AliPIDResponse.h"
+#include "AliESDv0KineCuts.h"
+#include "AliESDv0.h"
+
+#include "AliTPCPIDBase.h"
+
+/*
+This class is a base class for all other
+analysis tasks that use V0's.
+It provides basics for V0 identification.
+In addition, some further basic functions are provided.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+ClassImp(AliTPCPIDBase)
+
+Double_t AliTPCPIDBase::fgCutGeo = 1.;   
+Double_t AliTPCPIDBase::fgCutNcr = 0.85; 
+Double_t AliTPCPIDBase::fgCutNcl = 0.7;  
+
+UShort_t AliTPCPIDBase::fgCutPureNcl = 60;
+
+//________________________________________________________________________
+AliTPCPIDBase::AliTPCPIDBase()
+  : AliAnalysisTaskSE()
+  , fEvent(0x0)
+  , fESD(0x0)
+  , fMC(0x0)
+  , fPIDResponse(0x0)
+  , fV0KineCuts(0x0)
+  , fIsPbpOrpPb(kFALSE)
+  , fUsePhiCut(kFALSE)
+  , fTPCcutType(kNoCut)
+  , fZvtxCutEvent(10.0)
+  , fEtaCut(0.9)
+  , fPhiCutLow(0x0)
+  , fPhiCutHigh(0x0)
+  , fRandom(0x0)
+  , fTrackFilter(0x0)
+  , fNumTagsStored(0)
+  , fV0tags(0x0)
+  , fStoreMotherIndex(kFALSE)
+  , fV0motherIndex(0x0)
+{
+  // default Constructor
+  
+  fRandom = new TRandom3(0); // 0 means random seed
+
+
+  // Question: Is this the right place to initialize these functions?
+  // Will it work on proof? i.e. will they be streamed to the workers?
+  // Also one should add getters and setters
+  fPhiCutLow  = new TF1("StdPhiCutLow",  "0.1/x/x+pi/18.0-0.025", 0, 100);
+  fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.12/x+pi/18.0+0.035", 0, 100);
+}
+
+//________________________________________________________________________
+AliTPCPIDBase::AliTPCPIDBase(const char *name)
+  : AliAnalysisTaskSE(name)
+  , fEvent(0x0)
+  , fESD(0x0)
+  , fMC(0x0)
+  , fPIDResponse(0x0)
+  , fV0KineCuts(0x0)
+  , fIsPbpOrpPb(kFALSE)
+  , fUsePhiCut(kFALSE)
+  , fTPCcutType(kNoCut)
+  , fZvtxCutEvent(10.0)
+  , fEtaCut(0.9)
+  , fPhiCutLow(0x0)
+  , fPhiCutHigh(0x0)
+  , fRandom(0x0)
+  , fTrackFilter(0x0)
+  , fNumTagsStored(0)
+  , fV0tags(0x0)
+  , fStoreMotherIndex(kFALSE)
+  , fV0motherIndex(0x0)
+{
+  // Constructor
+  
+  fRandom = new TRandom3(0); // 0 means random seed
+
+  
+  // Question: Is this the right place to initialize these functions?
+  // Will it work on proof? i.e. will they be streamed to the workers?
+  // Also one should add getters and setters
+  fPhiCutLow  = new TF1("StdPhiCutLow",  "0.1/x/x+pi/18.0-0.025", 0, 100);
+  fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.12/x+pi/18.0+0.035", 0, 100);
+
+  DefineInput (0, TChain::Class());
+  DefineOutput(0,  TTree::Class());
+}
+
+
+//________________________________________________________________________
+AliTPCPIDBase::~AliTPCPIDBase()
+{
+  // dtor
+  
+  delete fPhiCutLow;
+  fPhiCutLow = 0;
+  
+  delete fPhiCutHigh;
+  fPhiCutHigh = 0;
+  
+  delete fTrackFilter;
+  fTrackFilter = 0;
+  
+  delete fRandom;
+  fRandom = 0;
+  
+  delete fV0KineCuts;
+  fV0KineCuts = 0;
+  
+  delete fV0tags;
+  fV0tags = 0;
+  fNumTagsStored = 0;
+  
+  delete fV0motherIndex;
+  fV0motherIndex = 0;
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDBase::UserCreateOutputObjects()
+{
+  // Create histograms
+  // Called once
+  
+  // Input hander
+  AliAnalysisManager* man = AliAnalysisManager::GetAnalysisManager();
+  AliInputEventHandler* inputHandler = dynamic_cast<AliInputEventHandler*>(man->GetInputEventHandler());
+  
+  if (!inputHandler) {
+    AliFatal("Input handler needed");
+    fPIDResponse = 0x0;
+    
+    return;
+  }
+
+  // PID response object
+  fPIDResponse = inputHandler->GetPIDResponse();
+  if (!fPIDResponse)
+    AliError("PIDResponse object was not created");
+  
+  // V0 Kine cuts 
+  fV0KineCuts = new AliESDv0KineCuts;
+  fV0KineCuts->SetGammaCutChi2NDF(5.);
+  // Only accept V0el with prod. radius within 45 cm -> PID will by systematically biased for larger values!
+  Float_t gammaProdVertexRadiusCuts[2] = { 3.0, 45. }; 
+  fV0KineCuts->SetGammaCutVertexR(&gammaProdVertexRadiusCuts[0]);
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDBase::UserExec(Option_t *)
+{
+  // Main loop
+  // Called for each event
+}      
+
+//________________________________________________________________________
+void AliTPCPIDBase::Terminate(const Option_t *)
+{
+  // Called once at the end of the query
+}
+
+
+//_____________________________________________________________________________
+Double_t AliTPCPIDBase::GetPhiPrime(Double_t phi, Double_t magField, Int_t charge) const
+{
+  // Get phiPrime which is the cut variable to reject high pT tracks near edges
+
+  if(magField < 0)    // for negatve polarity field
+    phi = TMath::TwoPi() - phi;
+    
+  if(charge < 0) // for negatve charge
+    phi = TMath::TwoPi() - phi;
+  
+  phi += TMath::Pi() / 18.0; // to center gap in the middle
+  phi = fmod(phi, TMath::Pi() / 9.0);
+  return phi;
+}
+
+
+//______________________________________________________________________________
+Bool_t AliTPCPIDBase::PhiPrimeCut(Double_t trackPt, Double_t trackPhi, Short_t trackCharge, Double_t magField) const
+{
+  // Apply phi' cut to given track parameters
+  
+  if (trackPt < 2.0)
+    return kTRUE;
+  
+  Double_t phiPrime = GetPhiPrime(trackPhi, magField, trackCharge);
+  
+  if (phiPrime < fPhiCutHigh->Eval(trackPt) && phiPrime > fPhiCutLow->Eval(trackPt))
+    return kFALSE; // reject track
+    
+    return kTRUE;
+}
+
+
+//______________________________________________________________________________
+Bool_t AliTPCPIDBase::PhiPrimeCut(const AliVTrack* track, Double_t magField) const
+{
+  return PhiPrimeCut(track->Pt(), track->Phi(), track->Charge(), magField);
+}
+
+
+//______________________________________________________________________________
+Bool_t AliTPCPIDBase::GetVertexIsOk(AliVEvent* event, Bool_t doVtxZcut) const
+{
+  // Check whether vertex ful-fills quality requirements.
+  // Apply cut on z-position of vertex if doVtxZcut = kTRUE.
+  
+  AliAODEvent* aod = 0x0;
+  AliESDEvent* esd = 0x0;
+  
+  aod = dynamic_cast<AliAODEvent*>(event);
+  if (!aod) {
+    esd = dynamic_cast<AliESDEvent*>(event);
+    
+    if (!esd) {
+      AliError("Event seems to be neither AOD nor ESD!");
+      return kFALSE;
+    }
+  }
+    
+  if (fIsPbpOrpPb) {
+    const AliVVertex* trkVtx = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertex()) :
+                                      dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertex()));
+      
+    if (!trkVtx || trkVtx->GetNContributors() <= 0)
+      return kFALSE;
+      
+    TString vtxTtl = trkVtx->GetTitle();
+    if (!vtxTtl.Contains("VertexerTracks"))
+      return kFALSE;
+      
+    Float_t zvtx = trkVtx->GetZ();
+    const AliVVertex* spdVtx = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertexSPD()) :
+                                      dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertexSPD()));
+    if (spdVtx->GetNContributors() <= 0)
+      return kFALSE;
+      
+    TString vtxTyp = spdVtx->GetTitle();
+    Double_t cov[6] = {0};
+    spdVtx->GetCovarianceMatrix(cov);
+    Double_t zRes = TMath::Sqrt(cov[5]);
+    if (vtxTyp.Contains("vertexer:Z") && (zRes > 0.25))
+      return kFALSE;
+      
+    if (TMath::Abs(spdVtx->GetZ() - trkVtx->GetZ()) > 0.5)
+      return kFALSE;
+    
+    if (doVtxZcut) {
+      if (TMath::Abs(zvtx) > fZvtxCutEvent) //Default: 10 cm
+        return kFALSE;
+    }
+      
+    return kTRUE;
+  }
+    
+  
+  // pp and PbPb
+  const AliVVertex* primaryVertex = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertex()) :
+                                           dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertexTracks()));
+    
+  if (!primaryVertex || primaryVertex->GetNContributors() <= 0)
+    return kFALSE;
+  
+  if (doVtxZcut) {
+    if (TMath::Abs(primaryVertex->GetZ()) > fZvtxCutEvent) //Default: 10 cm
+      return kFALSE;
+  }
+  
+  return kTRUE;
+}
+
+
+//______________________________________________________________________________
+void AliTPCPIDBase::FillV0PIDlist(AliESDEvent* event)
+{
+  //
+  // Fill the PID tag list
+  //
+
+  // If no event forwarded as parameter (default), cast current input event.
+  // Dynamic cast to ESD events (DO NOTHING for AOD events)
+  if (!event)
+    event = dynamic_cast<AliESDEvent *>(InputEvent());
+  
+  // If this fails, do nothing
+  if (!event) {
+    AliError("Failed to retrieve ESD event. No V0's processed (only works for ESDs by now).");
+    return;
+  }
+  
+  if (!fV0KineCuts) {
+    AliError("V0KineCuts not available!");
+    return;
+  }
+  
+  TString beamType(event->GetBeamType());
+  
+  if (beamType.CompareTo("Pb-Pb") == 0 || beamType.CompareTo("A-A") == 0) {
+    fV0KineCuts->SetMode(AliESDv0KineCuts::kPurity, AliESDv0KineCuts::kPbPb);
+  }
+  else {
+    fV0KineCuts->SetMode(AliESDv0KineCuts::kPurity, AliESDv0KineCuts::kPP); 
+  }
+
+  // V0 selection
+  // set event
+  fV0KineCuts->SetEvent(event);
+
+  const Int_t numTracks = event->GetNumberOfTracks();
+  fV0tags = new Char_t[numTracks];
+  for (Int_t i = 0; i < numTracks; i++)
+    fV0tags[i] = 0;
+  
+  if (fStoreMotherIndex)  {
+    fV0motherIndex = new Int_t[numTracks];
+    for (Int_t i = 0; i < numTracks; i++)
+      fV0motherIndex[i] = -1;
+  }
+  
+  fNumTagsStored = numTracks;
+  
+  // loop over V0 particles
+  for (Int_t iv0 = 0; iv0 < event->GetNumberOfV0s(); iv0++) {
+    AliESDv0* v0 = (AliESDv0*)event->GetV0(iv0);
+    if (!v0)
+      continue;
+    
+    // Reject onFly V0's <-> Only take V0's from offline V0 finder
+    if (v0->GetOnFlyStatus())
+      continue; 
+  
+    // Get the particle selection 
+    Bool_t foundV0 = kFALSE;
+    Int_t pdgV0 = 0, pdgP = 0, pdgN = 0;
+
+    foundV0 = fV0KineCuts->ProcessV0(v0, pdgV0, pdgP, pdgN);
+    if (!foundV0)
+      continue;
+    
+    Int_t iTrackP = v0->GetPindex();  // positive track
+    Int_t iTrackN = v0->GetNindex();  // negative track
+
+    
+    // Fill the Object arrays
+    // positive particles
+    if (pdgP == -11) {
+      fV0tags[iTrackP] = 14;
+    }
+    else if (pdgP == 211) {
+      fV0tags[iTrackP] = 15;
+    }
+    else if(pdgP == 2212) {
+      fV0tags[iTrackP] = 16;
+    }
+    
+    if (fStoreMotherIndex)
+      fV0motherIndex[iTrackP] = iv0;
+
+    // negative particles
+    if( pdgN == 11){
+      fV0tags[iTrackN] = -14;
+    }
+    else if( pdgN == -211){
+      fV0tags[iTrackN] = -15;
+    }
+    else if( pdgN == -2212){
+      fV0tags[iTrackN] = -16;
+    }
+    
+    if (fStoreMotherIndex)
+      fV0motherIndex[iTrackN] = iv0;
+  }
+}
+
+
+//______________________________________________________________________________
+void AliTPCPIDBase::ClearV0PIDlist()
+{
+  //
+  // Clear the PID tag list
+  //
+
+  delete fV0tags;
+  fV0tags = 0;
+  
+  delete fV0motherIndex;
+  fV0motherIndex = 0;
+  
+  fNumTagsStored = 0;
+}
+
+
+//______________________________________________________________________________
+Char_t AliTPCPIDBase::GetV0tag(Int_t trackIndex) const
+{
+  //
+  // Get the tag for the corresponding trackIndex. Returns -99 in case of invalid index/tag list.
+  //
+  
+  if (trackIndex < 0 || trackIndex >= fNumTagsStored || !fV0tags)
+    return -99;
+  
+  return fV0tags[trackIndex];
+}
+
+
+//______________________________________________________________________________
+Int_t AliTPCPIDBase::GetV0motherIndex(Int_t trackIndex) const
+{
+  //
+  // Get the index of the V0 mother for the corresponding trackIndex. Returns -99 in case of invalid index/mother index list.
+  //
+  
+  if (!fStoreMotherIndex || trackIndex < 0 || trackIndex >= fNumTagsStored || !fV0motherIndex)
+    return -99;
+  
+  return fV0motherIndex[trackIndex];
+}
+
+
+//________________________________________________________________________
+Bool_t AliTPCPIDBase::TPCCutMIGeo(const AliVTrack* track, const AliVEvent* evt, TTreeStream* streamer)
+{
+  //
+  // TPC Cut MIGeo
+  //
+
+  if (!track || !evt)
+    return kFALSE;
+  
+  const Short_t sign = track->Charge();
+  Double_t xyz[50];
+  Double_t pxpypz[50];
+  Double_t cv[100];
+
+  track->GetXYZ(xyz);
+  track->GetPxPyPz(pxpypz);
+
+  AliExternalTrackParam* par = new AliExternalTrackParam(xyz, pxpypz, cv, sign);
+  const AliESDtrack dummy;
+
+  const Double_t magField = evt->GetMagneticField();
+  Double_t varGeom = dummy.GetLengthInActiveZone(par, 3, 236, magField, 0, 0);
+  Double_t varNcr  = track->GetTPCClusterInfo(3, 1);
+  Double_t varNcls = track->GetTPCsignalN();
+
+  const Double_t varEval = 130. - 5. * TMath::Abs(1. / track->Pt());
+  Bool_t cutGeom   = varGeom > fgCutGeo * varEval;
+  Bool_t cutNcr    = varNcr  > fgCutNcr * varEval;
+  Bool_t cutNcls   = varNcls > fgCutNcl * varEval;
+
+  Bool_t kout = cutGeom && cutNcr && cutNcls;
+
+  if (streamer) {
+    Double_t dedx = track->GetTPCsignal();
+
+    (*streamer)<<"tree"<<
+      "param.="<< par<<
+      "varGeom="<<varGeom<<
+      "varNcr="<<varNcr<<
+      "varNcls="<<varNcls<<
+      
+      "cutGeom="<<cutGeom<<
+      "cutNcr="<<cutNcr<<
+      "cutNcls="<<cutNcls<<
+      
+      "kout="<<kout<<
+      "dedx="<<dedx<<
+      
+      "\n";
+  }
+  
+  delete par;
+  
+  return kout;
+}
+
+
+//________________________________________________________________________
+Bool_t AliTPCPIDBase::TPCnclCut(const AliVTrack* track)
+{
+  //
+  // TPC Cut on TPCsignalN() only
+  //
+
+  if (!track)
+    return kFALSE;
+  
+  return (track->GetTPCsignalN() >= fgCutPureNcl);
+}
diff --git a/PWGPP/TPC/AliTPCPIDBase.h b/PWGPP/TPC/AliTPCPIDBase.h
new file mode 100644 (file)
index 0000000..0a653c5
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef ALITPCPIDBASE_H
+#define ALITPCPIDBASE_H
+
+/*
+This class is a base class for all other
+analysis tasks that use V0's.
+It provides basics for V0 identification.
+In addition, some further basic functions are provided.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+class TF1;
+class TRandom3;
+class TObjArray;
+class AliVEvent;
+class AliESDEvent;
+class AliMCEvent;
+class AliPIDResponse;
+class AliESDv0KineCuts;
+class AliPID;
+class AliAnalysisFilter;
+class AliVTrack;
+
+#include <TTreeStream.h>
+#include "AliInputEventHandler.h"
+#include "AliTOFPIDResponse.h"
+#include "AliAnalysisTaskSE.h"
+
+class AliTPCPIDBase : public AliAnalysisTaskSE {
+ public:
+  enum TPCcutType { kNoCut = 0, kTPCCutMIGeo = 1, kTPCnclCut = 2 };
+  AliTPCPIDBase();
+  AliTPCPIDBase(const char *name);
+  virtual ~AliTPCPIDBase();
+  
+  virtual void   UserCreateOutputObjects();
+  virtual void   UserExec(Option_t *option);
+  virtual void   Terminate(const Option_t*);
+  
+  virtual Bool_t GetVertexIsOk(AliVEvent* event, Bool_t doVtxZcut = kTRUE) const;
+  
+  virtual Bool_t GetIsPbpOrpPb() const { return fIsPbpOrpPb; };
+  virtual void SetIsPbpOrpPb(Bool_t newValue) { fIsPbpOrpPb = newValue; };
+  
+  virtual Double_t GetZvtxCutEvent() const { return fZvtxCutEvent; };
+  virtual void SetZvtxCutEvent(Double_t newValue) { fZvtxCutEvent = newValue; };
+  
+  virtual Bool_t GetUsePhiCut() const { return fUsePhiCut; };
+  virtual void SetUsePhiCut(Bool_t newValue) { fUsePhiCut = newValue; };
+  
+  virtual TPCcutType GetTPCcutType() const { return fTPCcutType; };
+  virtual Bool_t GetUseTPCCutMIGeo() const { return (fTPCcutType == kTPCCutMIGeo); };
+  virtual Bool_t GetUseTPCnclCut() const { return (fTPCcutType == kTPCnclCut); };
+  
+  virtual void SetTPCcutType(TPCcutType newType) { fTPCcutType = newType; };
+  
+  virtual Double_t GetEtaCut() const { return fEtaCut; };     
+  virtual void  SetEtaCut(Double_t etaCut){ fEtaCut = etaCut; };
+  
+  virtual const AliAnalysisFilter* GetTrackFilter() const { return fTrackFilter; };
+  virtual void  SetTrackFilter(AliAnalysisFilter* trackF) {fTrackFilter = trackF;}
+  
+  virtual Char_t GetV0tag(Int_t trackIndex) const;
+  
+  virtual Bool_t GetStoreMotherIndex() const { return fStoreMotherIndex; };
+  virtual void SetStoreMotherIndex(Bool_t newValue) { fStoreMotherIndex = newValue; };
+  
+  virtual Int_t GetV0motherIndex(Int_t trackIndex) const;
+  
+  virtual Double_t GetPhiPrime(Double_t phi, Double_t magField, Int_t charge) const;
+  virtual Bool_t PhiPrimeCut(const AliVTrack* track, Double_t magField) const;
+  virtual Bool_t PhiPrimeCut(Double_t trackPt, Double_t trackPhi, Short_t trackCharge, Double_t magField) const;
+  virtual Float_t GetDeltaTOF(const AliVTrack *track, const AliTOFPIDResponse* tofPIDresponse, const Double_t* times, 
+                              AliPID::EParticleType type) const;
+  
+  static Double_t GetCutGeo() { return fgCutGeo; };
+  static Double_t GetCutNcr() { return fgCutNcr; };
+  static Double_t GetCutNcl() { return fgCutNcl; };
+  
+  static void SetCutGeo(Double_t value) { fgCutGeo = value; };
+  static void SetCutNcr(Double_t value) { fgCutNcr = value; };
+  static void SetCutNcl(Double_t value) { fgCutNcl = value; };
+  
+  static Bool_t TPCCutMIGeo(const AliVTrack* track, const AliVEvent* evt, TTreeStream* streamer = 0x0);
+  static Bool_t TPCCutMIGeo(const AliVTrack* track, const AliInputEventHandler* evtHandler, TTreeStream* streamer = 0x0)
+    { if (!evtHandler) return kFALSE; return TPCCutMIGeo(track, evtHandler->GetEvent(), streamer); };
+  
+  static UShort_t GetCutPureNcl() { return fgCutPureNcl; };
+  static void SetCutPureNcl(UShort_t value) { fgCutPureNcl = value; };
+  
+  static Bool_t TPCnclCut(const AliVTrack* track);
+  
+ protected:
+  static Double_t fgCutGeo;   // Cut variable for TPCCutMIGeo concerning geometry
+  static Double_t fgCutNcr; // Cut variable for TPCCutMIGeo concerning num crossed rows
+  static Double_t fgCutNcl;  // Cut variable for TPCCutMIGeo concerning num clusters
+  
+  static UShort_t fgCutPureNcl; // Cut variable for TPCnclCut
+  
+  AliVEvent   *fEvent;    //! VEvent object
+  AliESDEvent *fESD;      //! ESDEvent object, if ESD
+  AliMCEvent  *fMC;       //! MC object
+
+  AliPIDResponse *fPIDResponse;    //! PID response Handler
+  AliESDv0KineCuts *fV0KineCuts;       //! ESD V0 kine cuts
+  
+  Bool_t fIsPbpOrpPb;       // Pbp/pPb collision or something else?
+  Bool_t fUsePhiCut;        // Use cut on phi (useful for TPC)
+  TPCcutType fTPCcutType;   // Type of TPC cut to be used
+  Double_t fZvtxCutEvent;   // Vertex z cut for the event (cm)
+  Double_t fEtaCut;         // Eta cut
+  
+  TF1* fPhiCutLow;          // phi prime cut, low
+  TF1* fPhiCutHigh;         // phi prime cut, high
+  
+  TRandom3* fRandom;        //! Can be used to statistically determine the shape in the pt bins e.g.
+  
+  AliAnalysisFilter* fTrackFilter; // Track Filter
+  
+
+  Int_t fNumTagsStored;     // Number of entries of fV0tags
+  Char_t* fV0tags;         //! Pointer to array with tags for identified particles from V0 decays
+  
+  Bool_t fStoreMotherIndex; // Switch on/off storing the mother indices of V0 daughters
+  Int_t* fV0motherIndex; //! Pointer to array with index of the mother V0
+  
+ private:
+  void FillV0PIDlist(AliESDEvent* esdEvent = 0x0);
+  void ClearV0PIDlist();
+  
+  AliTPCPIDBase(const AliTPCPIDBase&); // not implemented
+  AliTPCPIDBase& operator=(const AliTPCPIDBase&); // not implemented
+  
+  ClassDef(AliTPCPIDBase, 1);
+};
+
+
+
+inline Float_t AliTPCPIDBase::GetDeltaTOF(const AliVTrack *track, const AliTOFPIDResponse* tofPIDresponse,
+                                                     const Double_t* times, AliPID::EParticleType type) const
+{
+  return (track->GetTOFsignal() - tofPIDresponse->GetStartTime(track->P()) - times[type]);
+}
+
+#endif
diff --git a/PWGPP/TPC/AliTPCPIDEtaQA.cxx b/PWGPP/TPC/AliTPCPIDEtaQA.cxx
new file mode 100644 (file)
index 0000000..e5fd1db
--- /dev/null
@@ -0,0 +1,608 @@
+#include "TChain.h"
+#include "TTree.h"
+#include "TF1.h"
+#include "TAxis.h"
+#include "TH1F.h"
+#include "THnSparse.h"
+
+#include "AliMCParticle.h"
+//#include "AliStack.h"
+
+#include "AliAnalysisTask.h"
+#include "AliAnalysisManager.h"
+
+#include "AliESDEvent.h"
+#include "AliMCEvent.h"
+#include "AliESDInputHandler.h"
+#include "AliInputEventHandler.h"
+
+#include "AliVVertex.h"
+#include "AliAnalysisFilter.h"
+#include "AliPID.h"
+#include "AliPIDResponse.h"
+#include "AliTPCPIDResponse.h"
+
+#include "AliTPCPIDEtaQA.h"
+
+/*
+This task determines the eta dependence of the TPC signal.
+For this purpose, only tracks fulfilling some standard quality cuts are taken into account.
+The obtained data can be used to derive the functional behaviour of the eta dependence.
+Such a function can be plugged into this task to correct for the eta dependence and to see
+if there is then really no eta dependence left.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+ClassImp(AliTPCPIDEtaQA)
+
+//________________________________________________________________________
+AliTPCPIDEtaQA::AliTPCPIDEtaQA()
+  : AliAnalysisTaskPIDV0base()
+  , fPtThresholdForPhiCut(2.0)
+  , fPhiCutSecondBranchLow(0x0)
+  , fPhiCutSecondBranchHigh(0x0)
+  , fhPIDdataAll(0x0)
+  //OLD clusterQA, fhNumClustersPhiPrimePtBeforeCut(0x0)
+  //OLD clusterQA, fhNumClustersPhiPrimePtAfterCut(0x0)
+  , fhPhiPrimeCutEfficiency(0x0)
+  , fOutputContainer(0x0)
+{
+  // default Constructor
+
+  // Question: Is this the right place to initialize these functions?
+  // Will it work on proof? i.e. will they be streamed to the workers?
+  // Also one should add getters and setters
+  //TODO fPhiCutLow  = new TF1("StandardPhiCutLow",  "0.1/x/x+pi/18.0-0.025", 0, 50);
+  //TODO fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.12/x+pi/18.0+0.035", 0, 50);
+  
+  //TODO NEW fPhiCutLow  = new TF1("StandardPhiCutLow",  "0.072/x+pi/18.0-0.035", 0, 50);
+  //TODO NEW fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.07/x/x+0.1/x+pi/18.0+0.035", 0, 50);
+  
+  fPhiCutSecondBranchLow  = new TF1("StandardPhiCutSecondBranchLow",  "NewStandardPhiCutLow - 2.*pi/18.", 0, 50);
+  fPhiCutSecondBranchHigh = new TF1("StandardPhiCutSecondBranchHigh", "0.07/x+pi/18.0+0.125 - 2.*pi/18.", 0, 50);
+}
+
+//________________________________________________________________________
+AliTPCPIDEtaQA::AliTPCPIDEtaQA(const char *name)
+  : AliAnalysisTaskPIDV0base(name)
+  , fPtThresholdForPhiCut(2.0)
+  , fPhiCutSecondBranchLow(0x0)
+  , fPhiCutSecondBranchHigh(0x0)
+  , fhPIDdataAll(0x0)
+  //OLD clusterQA, fhNumClustersPhiPrimePtBeforeCut(0x0)
+  //OLD clusterQA, fhNumClustersPhiPrimePtAfterCut(0x0)
+  , fhPhiPrimeCutEfficiency(0x0)
+  , fOutputContainer(0x0)
+{
+  // Constructor
+  
+  // Question: Is this the right place to initialize these functions?
+  // Will it work on proof? i.e. will they be streamed to the workers?
+  // Also one should add getters and setters
+  //TODO fPhiCutLow  = new TF1("StandardPhiCutLow",  "0.1/x/x+pi/18.0-0.025", 0, 50);
+  //TODO fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.12/x+pi/18.0+0.035", 0, 50);
+  
+  //TODO NEW fPhiCutLow  = new TF1("StandardPhiCutLow",  "0.072/x+pi/18.0-0.035", 0, 50);
+  //TODO NEW fPhiCutHigh = new TF1("StandardPhiCutHigh", "0.07/x/x+0.1/x+pi/18.0+0.035", 0, 50);
+  
+  fPhiCutSecondBranchLow  = new TF1("StandardPhiCutSecondBranchLow",  "StandardPhiCutLow - 2.*pi/18.", 0, 50);
+  fPhiCutSecondBranchHigh = new TF1("StandardPhiCutSecondBranchHigh", "0.07/x+pi/18.0+0.125 - 2.*pi/18.", 0, 50);
+
+  // Define input and output slots here
+  // Input slot #0 works with a TChain
+  DefineInput(0, TChain::Class());
+  // Output slot #1 writes into a TObjArray container
+  DefineOutput(1, TObjArray::Class());
+}
+
+
+//________________________________________________________________________
+AliTPCPIDEtaQA::~AliTPCPIDEtaQA()
+{
+  // dtor
+  
+  delete fOutputContainer;
+  fOutputContainer = 0;
+  
+  delete fPhiCutSecondBranchLow;
+  fPhiCutSecondBranchLow = 0;
+  
+  delete fPhiCutSecondBranchHigh;
+  fPhiCutSecondBranchHigh = 0;
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDEtaQA::UserCreateOutputObjects()
+{
+  // Create histograms
+  // Called once
+
+  AliAnalysisTaskPIDV0base::UserCreateOutputObjects();
+
+  OpenFile(1);
+  
+  fOutputContainer = new TObjArray(1);
+  fOutputContainer->SetName(GetName()) ;
+  fOutputContainer->SetOwner(kTRUE);
+  
+  const Int_t nEta = TMath::Nint(40 * fEtaCut);
+  
+  const Int_t nPtBins = 68;
+  Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+           0.5,  0.55, 0.6,  0.65, 0.7,  0.75, 0.8,  0.85, 0.9,  0.95,
+           1.0,  1.1 , 1.2,  1.3 , 1.4,  1.5 , 1.6,  1.7 , 1.8,  1.9 ,
+           2.0,  2.2 , 2.4,  2.6 , 2.8,  3.0 , 3.2,  3.4 , 3.6,  3.8 ,
+           4.0,  4.5 , 5.0,  5.5 , 6.0,  6.5 , 7.0,  8.0 , 9.0,  10.0,
+           11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0,
+           26.0, 28.0, 30.0, 32.0, 34.0, 36.0, 40.0, 45.0, 50.0 };
+    
+      
+  const Int_t nBins = 6;
+  // MC PID, SelectSpecies, P(TPC_inner), multiplicity, deltaPrimeSpecies, eta
+  
+  Int_t bins[nBins] = 
+    {  9,   4, nPtBins,    40, 201, nEta };
+  Double_t xmin[nBins] = 
+    {  0., 0.,      0.,    0., 0.5, -fEtaCut};
+  Double_t xmax[nBins] = 
+    {  9., 4.,    50.0, 20000, 2.0, fEtaCut };
+               
+  fhPIDdataAll = new THnSparseI("hPIDdataAll","", nBins, bins, xmin, xmax);
+  SetUpHist(fhPIDdataAll, binsPt);
+  fOutputContainer->Add(fhPIDdataAll);
+  
+  /*OLD clusterQA
+  const Int_t nBinsQA = 3;
+  // pT, phiPrime, Ncl
+  
+  Int_t binsQA[nBinsQA] = 
+    {  nPtBins, 50, 90 }; 
+  Double_t xminQA[nBinsQA] = 
+    {  0., 0., 70.};
+  Double_t xmaxQA[nBinsQA] = 
+    {  50.0, TMath::Pi() / 9., 160 };
+    
+  fhNumClustersPhiPrimePtBeforeCut = new THnSparseI("hNumClustersPhiPrimePtBeforeCut", "", nBinsQA, binsQA, xminQA, xmaxQA);
+  fhNumClustersPhiPrimePtBeforeCut->SetBinEdges(0, binsPt);
+  fhNumClustersPhiPrimePtBeforeCut->GetAxis(0)->SetTitle("p_{T} (GeV/c)");
+  fhNumClustersPhiPrimePtBeforeCut->GetAxis(1)->SetTitle("#phi'");
+  fhNumClustersPhiPrimePtBeforeCut->GetAxis(2)->SetTitle("Ncl");
+  fOutputContainer->Add(fhNumClustersPhiPrimePtBeforeCut);
+  
+  fhNumClustersPhiPrimePtAfterCut = new THnSparseI("hNumClustersPhiPrimePtAfterCut", "", nBinsQA, binsQA, xminQA, xmaxQA);
+  fhNumClustersPhiPrimePtAfterCut->SetBinEdges(0, binsPt);
+  fhNumClustersPhiPrimePtAfterCut->GetAxis(0)->SetTitle("p_{T} (GeV/c)");
+  fhNumClustersPhiPrimePtAfterCut->GetAxis(1)->SetTitle("#phi'");
+  fhNumClustersPhiPrimePtAfterCut->GetAxis(2)->SetTitle("Ncl");
+  fOutputContainer->Add(fhNumClustersPhiPrimePtAfterCut);
+  
+  fhPhiPrimeCutEfficiency = new TH1F("hPhiPrimeCutEfficiency", "Efficiency of #phi' cut;p_{T} (GeV/c);Cut efficiency", nPtBins, binsPt);
+  fOutputContainer->Add(fhPhiPrimeCutEfficiency);
+  */
+  
+  PostData(1, fOutputContainer);
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDEtaQA::UserExec(Option_t *)
+{
+  // Main loop
+  // Called for each event
+
+  fESD = dynamic_cast<AliESDEvent*>(InputEvent());
+  if (!fESD) {
+    Printf("ERROR: fESD not available");
+    return;
+  }
+  
+  fMC = dynamic_cast<AliMCEvent*>(MCEvent());
+  
+  if (!fPIDResponse || !fV0KineCuts)
+    return;
+  
+  if (!GetVertexIsOk(fESD))
+    return;
+  
+  const AliVVertex* primaryVertex = fESD->GetPrimaryVertexTracks(); 
+  if (!primaryVertex)
+    return;
+    
+  if(primaryVertex->GetNContributors() <= 0) 
+    return;
+  
+  Double_t magField = fESD->GetMagneticField();
+  
+  // Fill V0 arrays with V0s
+  FillV0PIDlist();
+  
+  Int_t multiplicity = fESD->GetNumberOfTracks();
+  
+  // Track loop to fill a Train spectrum
+  for (Int_t iTracks = 0; iTracks < fESD->GetNumberOfTracks(); iTracks++) {
+    AliESDtrack* track = fESD->GetTrack(iTracks);
+    if (!track) {
+      Printf("ERROR: Could not receive track %d", iTracks);
+      continue;
+    }
+    
+    if (TMath::Abs(track->Eta()) >= fEtaCut)  continue;
+    
+    //if (TMath::Abs(track->Eta()) < 0.6 || TMath::Abs(track->Eta()) > 0.8)  continue;
+    
+    // Do not distinguish positively and negatively charged V0's
+    Char_t v0tagAllCharges = TMath::Abs(GetV0tag(iTracks));
+    if (v0tagAllCharges == -99) {
+      AliError(Form("Problem with V0 tag list (requested V0 track for track %d from %d (list status %d))!", iTracks, fESD->GetNumberOfTracks(),
+                    fV0tags != 0x0));
+      continue;
+    }
+    
+    Bool_t isV0el = v0tagAllCharges == 14;
+    Bool_t isV0pi = v0tagAllCharges == 15;
+    Bool_t isV0pr = v0tagAllCharges == 16;
+    Bool_t isV0 = isV0el || isV0pi || isV0pr;
+    
+    // Apply track cut. Accept track, if from V0 or if accepted by cut.
+    // Do not apply the track cuts to V0s, since the track cuts are usually
+    // cuts on primaries, what will throw away almost all V0s.
+    if(!isV0 && (fTrackFilter && !fTrackFilter->IsSelected(track)))
+      continue;
+    
+    if (GetUseTPCCutMIGeo()) {
+      // If cut on geometry is active, don't cut on number of clusters, since such a cut is already included.
+      if (!isV0) {
+        if (!TPCCutMIGeo(track, InputEvent()))
+          continue;
+      }
+      else {
+        // One should not cut on geometry for V0's since they have different topology. (Loosely) cut on num of clusters instead.
+        if (track->GetTPCsignalN() < 60)
+          continue;
+      }
+    }
+    else {
+      // If cut on geometry is not active, always cut on num clusters
+      if (track->GetTPCsignalN() < 60)
+        continue;
+    }
+    
+    Double_t pT = track->Pt();
+    //Double_t phiPrime = GetPhiPrime(track->Phi(), magField, track->Charge());
+    
+    //OLD clusterQA Double_t entryQA[3] = { pT, phiPrime, track->GetTPCsignalN() };
+    //OLD clusterQA fhNumClustersPhiPrimePtBeforeCut->Fill(entryQA);
+
+    // Apply PhiPrimeCut only on high-pt tracks, in this case: Tracks with pt >= fPtThresholdForPhiCut
+    if(fUsePhiCut && pT >= fPtThresholdForPhiCut) {
+      
+      if(fUsePhiCut) {
+      if (!PhiPrimeCut(track, magField))
+        continue; // reject track
+    }
+      //Use cut for second branch?
+      if(!PhiPrimeCut(track, magField))
+      //if(!PhiPrimeCut(track, magField)
+      //   || (phiPrime < fPhiCutSecondBranchHigh->Eval(pT)  && phiPrime > fPhiCutSecondBranchLow->Eval(pT)))
+             continue; // reject track
+    }
+    
+    //OLD clusterQA fhNumClustersPhiPrimePtAfterCut->Fill(entryQA);
+    
+    Int_t label = track->GetLabel();
+
+    AliMCParticle* mcTrack = 0x0;
+    
+    if (fMC) {
+      if (label < 0)
+        continue; // If MC is available, reject tracks with negative ESD label
+      mcTrack = dynamic_cast<AliMCParticle*>(fMC->GetTrack(TMath::Abs(label)));
+      if (!mcTrack) {
+        Printf("ERROR: Could not receive mcTrack with label %d for track %d", label, iTracks);
+        continue;
+      }
+      
+      /*// Only accept MC primaries
+      if (!fMC->Stack()->IsPhysicalPrimary(TMath::Abs(label))) {
+        continue;
+      }*/
+    }
+    
+    // Momentum
+    Double_t pTPC = track->GetTPCmomentum();
+    
+    // Eta
+    Float_t eta = track->Eta();
+    
+    // TPC signal
+    Double_t dEdxTPC = track->GetTPCsignal();
+    
+    Double_t dEdxExpectedEl = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kElectron, AliTPCPIDResponse::kdEdxDefault, 
+                                                                              fPIDResponse->UseTPCEtaCorrection(),
+                                                                              fPIDResponse->UseTPCMultiplicityCorrection());
+    Double_t dEdxExpectedKa = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kKaon, AliTPCPIDResponse::kdEdxDefault, 
+                                                                              fPIDResponse->UseTPCEtaCorrection(),
+                                                                              fPIDResponse->UseTPCMultiplicityCorrection());
+    Double_t dEdxExpectedPi = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kPion, AliTPCPIDResponse::kdEdxDefault, 
+                                                                              fPIDResponse->UseTPCEtaCorrection(),
+                                                                              fPIDResponse->UseTPCMultiplicityCorrection());
+    Double_t dEdxExpectedPr = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kProton, AliTPCPIDResponse::kdEdxDefault, 
+                                                                              fPIDResponse->UseTPCEtaCorrection(),
+                                                                              fPIDResponse->UseTPCMultiplicityCorrection());
+    
+    Double_t deltaPrimeEl = (dEdxExpectedEl > 0) ? (dEdxTPC / dEdxExpectedEl) : (-1);
+    Double_t deltaPrimeKa = (dEdxExpectedKa > 0) ? (dEdxTPC / dEdxExpectedKa) : (-1);
+    Double_t deltaPrimePi = (dEdxExpectedPi > 0) ? (dEdxTPC / dEdxExpectedPi) : (-1);
+    Double_t deltaPrimePr = (dEdxExpectedPr > 0) ? (dEdxTPC / dEdxExpectedPr) : (-1);
+    
+    Int_t binMC = -1;
+    if (fMC) {
+      Int_t pdg = mcTrack->PdgCode();
+      
+      if (TMath::Abs(pdg) == 211) {//Pion
+        // If V0, only accept if correctly identified
+        if (isV0)   {
+          if (isV0pi)
+            binMC = 7;
+          else
+            continue;
+        }
+        else
+          binMC = 3;
+      }
+      else if (TMath::Abs(pdg) == 321) {//Kaon
+        // No kaons from V0 => Reject
+        if (isV0)
+          continue;
+        else
+          binMC = 1;
+      }
+      else if (TMath::Abs(pdg) == 2212) {//Proton
+        // If V0, only accept if correctly identified
+        if (isV0)   {
+          if (isV0pr)
+            binMC = 8;
+          else
+            continue;
+        }
+        else
+          binMC = 4;
+      }
+      else if (TMath::Abs(pdg) == 11) {//Electron
+        // If V0, only accept if correctly identified
+        if (isV0)   {
+          if (isV0el)
+            binMC = 6;
+          else
+            continue;
+        }
+        else
+          binMC = 0;
+      }
+      else if (TMath::Abs(pdg) == 13) {//Muon
+        // No muons from V0 => Reject
+        if (isV0)
+          continue;
+        else
+          binMC = 2;
+      }
+      else
+        continue; // Reject all other particles
+    }
+    
+    Bool_t isKaon = kFALSE;
+    Bool_t isPion = kFALSE;
+    Bool_t isProton = kFALSE;
+    Bool_t isElectron = kFALSE;
+    Bool_t isV0plusTOFel = kFALSE;
+    
+    
+    if (!fMC && !isV0) {    
+      UInt_t status = track->GetStatus();
+      Bool_t hasTOFout  = status&AliESDtrack::kTOFout; 
+      Bool_t hasTOFtime = status&AliESDtrack::kTIME;
+      Bool_t hasTOF     = kFALSE;
+      if (hasTOFout && hasTOFtime)
+        hasTOF = kTRUE;
+      Float_t length = track->GetIntegratedLength();
+      // Length check only for primaries, not for V0's!
+      if (length < 350 && !isV0)
+        hasTOF = kFALSE;
+
+    
+      // Select Kaons, Pions and Protons in 3 sigma band (TOF and TPC). Below some momentum threshold, only use TPC cut,
+      // since TOF efficiency is really bad.
+      // In case of ambiguity, add entries for corresponding species
+      if ((pTPC <= 0.25 && TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kKaon)) < 6.0) ||
+          (pTPC > 0.25 && pTPC <= 0.3 && TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kKaon)) < 4.0) ||
+          (pTPC > 0.3 && pTPC <= 0.35 && TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kKaon)) < 3.0) ||
+          (pTPC > 0.35 && TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kKaon)) < 3.0 && hasTOF && 
+           TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kKaon)) < 3.0)) {
+          isKaon = kTRUE;
+      }
+      if ((dEdxTPC >= 50. / TMath::Power(pTPC, 1.3)) && // Pattern recognition instead of TPC cut to be ~independent of old TPC expected dEdx
+         ((pTPC < 0.6) ||
+         (pTPC >= 0.6 && hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kProton)) < 3.0))) {
+        isProton = kTRUE;
+      }
+      /*
+      if ((TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kProton)) < (pTPC < 0.3 ? 8.0 : 5.0)) &&
+          ((pTPC < 0.6) ||
+          (pTPC >= 0.6 && hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kProton)) < 3.0))) {
+          isProton = kTRUE;
+      }*/
+      if (TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kPion)) < 3.0 &&
+          (pTPC <= 0.3 || 
+           (pTPC > 0.3 && hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kPion)) < 3.0)))  {
+          isPion = kTRUE;
+      }
+      if (TMath::Abs(fPIDResponse->NumberOfSigmasTPC(track, AliPID::kElectron)) < 3.0 &&
+          (pTPC <= 0.3 ||
+           (pTPC > 0.3 && hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kElectron)) < 3.0)))  {
+          isElectron = kTRUE;
+      }
+    }
+    else if (!fMC && isV0el) {
+      // Special treatment for V0 electrons -> Look for V0+TOF electrons
+      
+      UInt_t status = track->GetStatus();
+      Bool_t hasTOFout  = status&AliESDtrack::kTOFout; 
+      Bool_t hasTOFtime = status&AliESDtrack::kTIME;
+      Bool_t hasTOF     = kFALSE;
+      if (hasTOFout && hasTOFtime)
+        hasTOF = kTRUE;
+      
+      // No length check for V0's
+      if (hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kElectron)) < 3.0) {
+        isV0plusTOFel = kTRUE;
+      }      
+    }
+    
+    // MC PID, SelectSpecies, P(TPC_inner), multiplicity, deltaPrimeSpecies, eta
+    Double_t entry[6] = { binMC, 0, pTPC, multiplicity, deltaPrimeEl, eta };
+    
+    if (fMC)  {
+      fhPIDdataAll->Fill(entry);
+      
+      entry[1] = 1;
+      entry[4] = deltaPrimeKa;
+      fhPIDdataAll->Fill(entry);
+      
+      entry[1] = 2;
+      entry[4] = deltaPrimePi;
+      fhPIDdataAll->Fill(entry);
+      
+      entry[1] = 3;
+      entry[4] = deltaPrimePr;
+      fhPIDdataAll->Fill(entry);
+    }
+    else  {      
+      for (Int_t i = 0; i < 8; i++) {
+        if (i == 0 && isKaon) 
+          binMC = 1;
+        else if (i == 1 && isPion)  
+          binMC = 3;
+        else if (i == 2 && isProton)  
+          binMC = 4;
+        else if (i == 3 && isElectron)
+          binMC = 0;
+        else if (i == 4 && isV0plusTOFel)
+          binMC = 5;
+        else if (i == 5 && isV0el)
+          binMC = 6;
+        else if (i == 6 && isV0pi)
+          binMC = 7;
+        else if (i == 7 && isV0pr)
+          binMC = 8;
+        else
+          continue;
+          
+        entry[0] = binMC;
+        
+        entry[1] = 0;
+        entry[4] = deltaPrimeEl;
+        fhPIDdataAll->Fill(entry);
+        
+        entry[1] = 1;
+        entry[4] = deltaPrimeKa;
+        fhPIDdataAll->Fill(entry);
+        
+        entry[1] = 2;
+        entry[4] = deltaPrimePi;
+        fhPIDdataAll->Fill(entry);
+        
+        entry[1] = 3;
+        entry[4] = deltaPrimePr;
+        fhPIDdataAll->Fill(entry);
+      }
+    }
+  } //track loop 
+
+  // Post output data.
+  PostData(1, fOutputContainer);
+  
+  // Clear the V0 PID arrays
+  ClearV0PIDlist();
+}      
+
+//________________________________________________________________________
+void AliTPCPIDEtaQA::Terminate(const Option_t *)
+{
+  // Called once at the end of the query
+  /*OLD clusterQA
+  TObjArray* output = (TObjArray*)GetOutputData(1);
+  if (!output)
+    return;
+  
+  TH1F* hPhiPrimeCutEfficiency = (TH1F*)(output->FindObject("hPhiPrimeCutEfficiency"));
+  if (!hPhiPrimeCutEfficiency) 
+    return;
+  
+  THnSparse* hNumClustersPhiPrimePtBeforeCut = (THnSparse*)(output->FindObject("hNumClustersPhiPrimePtBeforeCut"));
+  if (!hNumClustersPhiPrimePtBeforeCut)
+    return;
+  
+  THnSparse* hNumClustersPhiPrimePtAfterCut = (THnSparse*)(output->FindObject("hNumClustersPhiPrimePtAfterCut"));
+  if (!hNumClustersPhiPrimePtAfterCut)
+    return;
+  
+  TH1D* hNumEntriesBefore = hNumClustersPhiPrimePtBeforeCut->Projection(0);
+  TH1D* hNumEntriesAfter = hNumClustersPhiPrimePtAfterCut->Projection(0);
+   
+  for (Int_t bin = 1; bin <= hNumEntriesBefore->GetXaxis()->GetNbins(); bin++)  {
+    Double_t numEntriesBefore = hNumEntriesBefore->GetBinContent(bin);
+    
+    if (numEntriesBefore > 0) {
+      Double_t numEntriesAfter = hNumEntriesAfter->GetBinContent(bin);
+      hPhiPrimeCutEfficiency->SetBinContent(bin,  numEntriesAfter / numEntriesBefore);
+      // Errors are 100%-correlated: Accepted tracks should be a constant fraction of all tracks in given bin.
+      // Thus: Only take statistical fluctuation from accepted tracks as error
+      hPhiPrimeCutEfficiency->SetBinError(bin, TMath::Sqrt(numEntriesAfter) / numEntriesBefore);
+      //                          (numEntriesAfter / numEntriesBefore) * TMath::Sqrt(1. / numEntriesAfter + 1. / numEntriesBefore));
+    }
+  }
+  
+  hPhiPrimeCutEfficiency->SetDrawOption("e");
+  */
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDEtaQA::SetUpHist(THnSparse* hist, Double_t* binsPt) const
+{
+  // Sets bin limits for axes which are not standard binned and the axes titles.
+  // MC PID, SelectSpecies, P(TPC_inner), multiplicity, deltaPrimeSpecies, eta
+  hist->SetBinEdges(2, binsPt);
+                          
+  // Set axes titles
+  hist->GetAxis(0)->SetTitle("MC PID");
+  hist->GetAxis(0)->SetBinLabel(1, "e");
+  hist->GetAxis(0)->SetBinLabel(2, "K");
+  hist->GetAxis(0)->SetBinLabel(3, "#mu");
+  hist->GetAxis(0)->SetBinLabel(4, "#pi");
+  hist->GetAxis(0)->SetBinLabel(5, "p");
+  hist->GetAxis(0)->SetBinLabel(6, "V0+TOF e");
+  hist->GetAxis(0)->SetBinLabel(7, "V0 e");
+  hist->GetAxis(0)->SetBinLabel(8, "V0 #pi");
+  hist->GetAxis(0)->SetBinLabel(9, "V0 p");
+  
+  hist->GetAxis(1)->SetTitle("Select Species");
+  hist->GetAxis(1)->SetBinLabel(1, "e");
+  hist->GetAxis(1)->SetBinLabel(2, "K");
+  hist->GetAxis(1)->SetBinLabel(3, "#pi");
+  hist->GetAxis(1)->SetBinLabel(4, "p");
+  
+  hist->GetAxis(2)->SetTitle("p_{TPC_inner} (GeV/c)");
+  
+  hist->GetAxis(3)->SetTitle("Event multiplicity");
+  
+  hist->GetAxis(4)->SetTitle("TPC #Delta'{species}");
+  
+  hist->GetAxis(5)->SetTitle("#eta");
+     
+  //hist->Sumw2();
+}
diff --git a/PWGPP/TPC/AliTPCPIDEtaQA.h b/PWGPP/TPC/AliTPCPIDEtaQA.h
new file mode 100644 (file)
index 0000000..5c38181
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef ALITPCPIDETAQA_H
+#define ALITPCPIDETAQA_H
+
+/*
+This task determines the eta dependence of the TPC signal.
+For this purpose, only tracks fulfilling some standard quality cuts are taken into account.
+The obtained data can be used to derive the functional behaviour of the eta dependence.
+Such a function can be plugged into this task to correct for the eta dependence and to see
+if there is then really no eta dependence left.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+class TH1F;
+class TF1;
+class THnSparse;
+class AliPIDResponse;
+class AliPID;
+
+#include "AliTPCPIDBase.h"
+
+class AliTPCPIDEtaQA : public AliTPCPIDBase {
+ public:
+  AliTPCPIDEtaQA();
+  AliTPCPIDEtaQA(const char *name);
+  virtual ~AliTPCPIDEtaQA();
+  
+  virtual void   UserCreateOutputObjects();
+  virtual void   UserExec(Option_t *option);
+  virtual void   Terminate(const Option_t*);
+  
+  Double_t GetPtThresholdForPhiCut() const { return fPtThresholdForPhiCut; };     
+  virtual void  SetPtThresholdForPhiCut(Double_t threshold) { fPtThresholdForPhiCut = threshold; };
+  
+ private:
+  virtual void   SetUpHist(THnSparse* hist, Double_t* binsPt) const;
+
+  Double_t fPtThresholdForPhiCut; // Only apply phi cut on tracks with pT larger equal this threshold
+  
+  TF1* fPhiCutSecondBranchLow; // phi prime cut, low, second branch (very low pT)
+  TF1* fPhiCutSecondBranchHigh; // phi prime cut, high, second branch (very low pT)
+    
+  THnSparseI* fhPIDdataAll; //! data histogram
+  
+  //OLD clusterQA THnSparseI* fhNumClustersPhiPrimePtBeforeCut; //! QA histogra - before phi prime cut
+  //OLD clusterQA THnSparseI* fhNumClustersPhiPrimePtAfterCut; //! QA histogra - after phi prime cut
+  
+  TH1F* fhPhiPrimeCutEfficiency; //! Effeciency of phiPrime cut as a functio of pT
+  
+  TObjArray* fOutputContainer; //! output data container
+   
+  AliTPCPIDEtaQA(const AliTPCPIDEtaQA&); // not implemented
+  AliTPCPIDEtaQA& operator=(const AliTPCPIDEtaQA&); // not implemented
+  
+  ClassDef(AliTPCPIDEtaQA, 1); // example of analysis
+};
+
+#endif
diff --git a/PWGPP/TPC/AliTPCPIDEtaTree.cxx b/PWGPP/TPC/AliTPCPIDEtaTree.cxx
new file mode 100644 (file)
index 0000000..a1f62ba
--- /dev/null
@@ -0,0 +1,548 @@
+#include "TChain.h"
+#include "TTree.h"
+#include "TF1.h"
+#include "TAxis.h"
+#include "TH2I.h"
+
+#include "THnSparse.h"
+
+#include "AliMCParticle.h"
+//#include "AliStack.h"
+
+#include "AliAnalysisTask.h"
+#include "AliAnalysisManager.h"
+
+#include "AliESDEvent.h"
+#include "AliMCEvent.h"
+#include "AliESDInputHandler.h"
+#include "AliInputEventHandler.h"
+
+#include "AliVVertex.h"
+#include "AliAnalysisFilter.h"
+#include "AliPID.h"
+#include "AliPIDResponse.h"
+#include "AliTPCPIDResponse.h"
+//#include "AliTPCParamSR.h"
+
+#include "AliTPCPIDEtaTree.h"
+
+/*
+This task determines the eta dependence of the TPC signal.
+For this purpose, only tracks fulfilling some standard quality cuts are taken into account.
+The obtained data can be used to derive the functional behaviour of the eta dependence.
+Such a function can be plugged into this task to correct for the eta dependence and to see
+if there is then really no eta dependence left.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+ClassImp(AliTPCPIDEtaTree)
+
+//________________________________________________________________________
+AliTPCPIDEtaTree::AliTPCPIDEtaTree()
+  : AliAnalysisTaskPIDV0base()
+  , fStoreMultiplicity(kFALSE)
+  , fStoreNumOfSubthresholdclusters(kFALSE)
+  , fStoreNumClustersInActiveVolume(kFALSE)
+  , fDoAdditionalQA(kFALSE)
+  , fPtpcPionCut(1.0)
+  , fPtpc(0)
+  , fPt(0)
+  , fDeDx(0)
+  , fDeDxExpected(0)
+  , fTanTheta(0)
+  //, fSinAlpha(0)
+  //, fY(0)
+  , fPhiPrime(0)
+  , fTPCsignalN(0)
+  , fTPCsignalNsubthreshold(0)
+  , fNumTPCClustersInActiveVolume(0)
+  , fPIDtype(0)
+  , fMultiplicity(0)
+  , fCorrectdEdxEtaDependence(kFALSE)
+  , fCorrectdEdxMultiplicityDependence(kFALSE)
+  , fTree(0x0)
+  , fTreePions(0x0)
+  , fOutputContainer(0x0)
+  , fhTOFqa(0x0)
+  , fhMultiplicityQA(0x0)
+{
+  // default Constructor
+}
+
+//________________________________________________________________________
+AliTPCPIDEtaTree::AliTPCPIDEtaTree(const char *name)
+  : AliAnalysisTaskPIDV0base(name)
+  , fStoreMultiplicity(kFALSE)
+  , fStoreNumOfSubthresholdclusters(kFALSE)
+  , fStoreNumClustersInActiveVolume(kFALSE)
+  , fDoAdditionalQA(kFALSE)
+  , fPtpcPionCut(1.0)
+  , fPtpc(0)
+  , fPt(0)
+  , fDeDx(0)
+  , fDeDxExpected(0)
+  , fTanTheta(0)
+  //, fSinAlpha(0)
+  //, fY(0)
+  , fPhiPrime(0)
+  , fTPCsignalN(0)
+  , fTPCsignalNsubthreshold(0)
+  , fNumTPCClustersInActiveVolume(0)
+  , fPIDtype(0)
+  , fMultiplicity(0)
+  , fCorrectdEdxEtaDependence(kFALSE)
+  , fCorrectdEdxMultiplicityDependence(kFALSE)
+  , fTree(0x0)
+  , fTreePions(0x0)
+  , fOutputContainer(0x0)
+  , fhTOFqa(0x0)
+  , fhMultiplicityQA(0x0)
+{
+  // Constructor
+
+  // Define input and output slots here
+  DefineInput(0, TChain::Class());
+  DefineOutput(1, TTree::Class());
+  DefineOutput(2, TTree::Class());
+  DefineOutput(3, TObjArray::Class());
+}
+
+
+//________________________________________________________________________
+AliTPCPIDEtaTree::~AliTPCPIDEtaTree()
+{
+  // dtor
+  
+  //delete fTree;
+  //fTree = 0x0;
+  
+  delete fOutputContainer;
+  fOutputContainer = 0x0;
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDEtaTree::UserCreateOutputObjects()
+{
+  // Create histograms
+  // Called once
+  
+  AliAnalysisTaskPIDV0base::UserCreateOutputObjects();
+  
+  if (fDoAdditionalQA) {
+    OpenFile(3);
+    
+    fOutputContainer = new TObjArray(2);
+    fOutputContainer->SetName(GetName());
+    fOutputContainer->SetOwner(kTRUE);
+    
+    const Int_t nBins = 6;
+    // p_vert, p_TPC, eta, nSigmaTOF, beta, multiplicity
+    Int_t bins[nBins] =    {   48,  48,    9,   30, 110,    4  };
+    Double_t xmin[nBins] = {  0.3, 0.3, -0.9, -5.0, 0.0,   0.  };
+    Double_t xmax[nBins] = {  2.7, 2.7,  0.9,  5.0, 1.1, 3200  };
+    
+    
+    fhTOFqa = new THnSparseI("hTOFqa","", nBins, bins, xmin, xmax);
+    fhTOFqa->GetAxis(0)->SetTitle("p_{Vertex} (GeV/c)");
+    fhTOFqa->GetAxis(1)->SetTitle("p_{TPC_inner} (GeV/c)");
+    fhTOFqa->GetAxis(2)->SetTitle("#eta");
+    fhTOFqa->GetAxis(3)->SetTitle("n #sigma_{p} TOF");
+    fhTOFqa->GetAxis(4)->SetTitle("#beta");
+    fhTOFqa->GetAxis(5)->SetTitle("Multiplicity");
+    
+    fOutputContainer->Add(fhTOFqa);
+    
+    
+    
+    fhMultiplicityQA = new TH2I("hMultiplicityQA", "Multiplicity check; Contributors to primary vertex per event; Total number of tracks per Event",
+                                120, 0, 6000, 400, 0, 20000);
+    fOutputContainer->Add(fhMultiplicityQA);
+  }
+  else {
+    fOutputContainer = new TObjArray(1);
+    fOutputContainer->SetName(GetName());
+    fOutputContainer->SetOwner(kTRUE);
+  }
+  
+  OpenFile(1);
+  
+  fTree = new TTree("fTree", "Tree for analysis of #eta dependence of TPC signal");
+  fTree->Branch("pTPC", &fPtpc);
+  //fTree->Branch("pT", &fPt);
+  fTree->Branch("dEdx", &fDeDx);
+  fTree->Branch("dEdxExpected", &fDeDxExpected);
+  fTree->Branch("tanTheta", &fTanTheta);
+  //fTree->Branch("sinAlpha", &fSinAlpha);
+  //fTree->Branch("y", &fY);
+  //TODO fTree->Branch("phiPrime", &fPhiPrime);
+  fTree->Branch("tpcSignalN", &fTPCsignalN);
+  
+  if (fStoreNumOfSubthresholdclusters)
+    fTree->Branch("tpcSignalNsubthreshold", &fTPCsignalNsubthreshold);
+  
+  if (fStoreNumClustersInActiveVolume)
+    fTree->Branch("numTPCClustersInActiveVolume", &fNumTPCClustersInActiveVolume);
+  
+  fTree->Branch("pidType", &fPIDtype);
+  
+  if (fStoreMultiplicity)  {
+    fTree->Branch("fMultiplicity", &fMultiplicity);
+  }
+  
+  OpenFile(2);
+  
+  fTreePions = new TTree("fTreePions", "Tree for analysis of #eta dependence of TPC signal - Pions");
+  fTreePions->Branch("pTPC", &fPtpc);
+  fTreePions->Branch("pT", &fPt);
+  fTreePions->Branch("dEdx", &fDeDx);
+  fTreePions->Branch("dEdxExpected", &fDeDxExpected);
+  fTreePions->Branch("tanTheta", &fTanTheta);
+  fTreePions->Branch("tpcSignalN", &fTPCsignalN);
+  
+  if (fStoreNumOfSubthresholdclusters)
+    fTreePions->Branch("tpcSignalNsubthreshold", &fTPCsignalNsubthreshold);
+  
+  if (fStoreMultiplicity)  {
+    fTreePions->Branch("fMultiplicity", &fMultiplicity);
+  }
+  
+  PostData(1, fTree);
+  PostData(2, fTreePions);
+  PostData(3, fOutputContainer);
+}
+
+
+//________________________________________________________________________
+void AliTPCPIDEtaTree::UserExec(Option_t *)
+{
+  // Main loop
+  // Called for each event
+  
+  fESD = dynamic_cast<AliESDEvent*>(InputEvent());
+  if (!fESD) {
+    Printf("ERROR: fESD not available");
+    return;
+  }
+  
+  fMC = dynamic_cast<AliMCEvent*>(MCEvent());
+  
+  if (!fPIDResponse || !fV0KineCuts)
+    return;
+  
+  if (!GetVertexIsOk(fESD))
+    return;
+
+  const AliVVertex* primaryVertex = fESD->GetPrimaryVertexTracks(); 
+  if (!primaryVertex)
+    return;
+  //fMultiplicity = primaryVertex->GetNContributors();
+  
+  
+  if(primaryVertex->GetNContributors() <= 0) 
+    return;
+  
+  fMultiplicity = fESD->GetNumberOfTracks(); 
+    
+  if (fDoAdditionalQA) {
+    Int_t nTotTracks = fESD->GetNumberOfTracks();
+    fhMultiplicityQA->Fill(primaryVertex->GetNContributors(), nTotTracks);
+  }
+  
+  Double_t magField = fESD->GetMagneticField();
+  
+  // Fill V0 arrays with V0s
+  FillV0PIDlist();
+  
+  //AliTPCParamSR par;
+  //par.Update();
+  
+  // Track loop to fill a Train spectrum
+  for (Int_t iTracks = 0; iTracks < fESD->GetNumberOfTracks(); iTracks++) {
+    Bool_t isPr = kFALSE;
+    Bool_t isPi = kFALSE;
+  
+    AliESDtrack* track = fESD->GetTrack(iTracks);
+    if (!track) {
+      Printf("ERROR: Could not receive track %d", iTracks);
+      continue;
+    }
+    
+    if (TMath::Abs(track->Eta()) > fEtaCut)
+      continue;
+    
+    // Do not distinguish positively and negatively charged V0's
+    Char_t v0tagAllCharges = TMath::Abs(GetV0tag(iTracks));
+    if (v0tagAllCharges == -99) {
+      AliError(Form("Problem with V0 tag list (requested V0 track for track %d from %d (list status %d))!", iTracks, fESD->GetNumberOfTracks(),
+                    fV0tags != 0x0));
+      continue;
+    }
+    
+    Bool_t isV0prNotMC = (v0tagAllCharges == 16) && !fMC; // Only accept V0 protons for data, not for MC
+    Bool_t isV0piNotMC = (v0tagAllCharges == 15) && !fMC; // Only accept V0 pions for data, not for MC
+    
+    Bool_t isV0NotMC = isV0prNotMC || isV0piNotMC;
+    
+    // Apply track cut
+    if(!isV0NotMC && fTrackFilter && !fTrackFilter->IsSelected(track))
+      continue;
+    
+    // Note: For V0's, the cut on ncl can be done via the tree (value is stored). One should not cut on geometry
+    // for V0's since they have different topology
+    if (!isV0NotMC && GetUseTPCCutMIGeo()) {
+      if (!TPCCutMIGeo(track, InputEvent()))
+        continue;
+    }
+    
+    
+    fPtpc = track->GetTPCmomentum();
+
+    if (fPtpc > 5.)
+      continue;
+    
+    fPt = track->Pt();
+    fPhiPrime = GetPhiPrime(track->Phi(), magField, track->Charge());
+    
+    Int_t label = track->GetLabel();
+
+    AliMCParticle* mcTrack = 0x0;
+    
+    if (fMC) {
+      if (label < 0)
+        continue; // If MC is available, reject tracks with negative ESD label
+      mcTrack = dynamic_cast<AliMCParticle*>(fMC->GetTrack(TMath::Abs(label)));
+      if (!mcTrack) {
+        Printf("ERROR: Could not receive mcTrack with label %d for track %d", label, iTracks);
+        continue;
+      }
+      
+      /*// Only accept MC primaries
+      if (!fMC->Stack()->IsPhysicalPrimary(TMath::Abs(label))) {
+        continue;
+      }*/
+    }
+
+    if (fMC) {
+      Int_t pdg = mcTrack->PdgCode();
+      
+      if (TMath::Abs(pdg) == 2212) // Proton
+        isPr = kTRUE;
+      else if ((pdg == 111 || TMath::Abs(pdg) == 211) && fPtpc <= fPtpcPionCut) // Pion below desired momentum threshold
+        isPi = kTRUE;
+      else
+        continue; // Only take protons and pions
+      
+      fPIDtype = kMCid;
+      /*
+      if (pdg == 111 || TMath::Abs(pdg) == 211) {//Pion
+        binMC = 3;
+      }
+      else if (TMath::Abs(pdg) == 311 || TMath::Abs(pdg) == 321) {//Kaon
+        binMC = 1;
+      }
+      else if (TMath::Abs(pdg) == 2212) {//Proton
+        binMC = 4;
+      }
+      else if (TMath::Abs(pdg) == 11) {//Electron
+        binMC = 0;
+      }
+      else if (TMath::Abs(pdg) == 13) {//Muon
+        binMC = 2;
+      }*/
+    }
+    
+    fDeDx = track->GetTPCsignal();
+    
+    UInt_t status = track->GetStatus();
+    Bool_t hasTOFout  = status&AliESDtrack::kTOFout; 
+    Bool_t hasTOFtime = status&AliESDtrack::kTIME;
+    Bool_t hasTOF     = kFALSE;
+    if (hasTOFout && hasTOFtime)
+      hasTOF = kTRUE;
+    Float_t length = track->GetIntegratedLength();
+    // Length check only for primaries, not for V0's!
+    if (length < 350 && !isV0NotMC)
+      hasTOF = kFALSE;
+      
+    if (!fMC) {    
+      // Note: Normally, the track cuts include a cut on primaries. Therefore, if the particle is a V0, it would usually
+      // NOT be found via the primary cuts. This means that the PIDtype describes more or less disjoint sets
+      if (isV0prNotMC) {
+        // V0
+        isPr = kTRUE;
+        
+        if (!hasTOF) {
+          fPIDtype = kV0idNoTOF;
+        }
+        else {
+          if (TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kProton)) < 3.0)
+            fPIDtype = kV0idPlusTOFaccepted;
+          else 
+            fPIDtype = kV0idPlusTOFrejected;
+        }
+      }
+      else if (isV0piNotMC) {
+        // V0 pion
+        if (fPtpc > fPtpcPionCut || fDeDx > 140.) // Reject pions above desired momentum threshold and also reject too high dEdx
+          continue;
+        
+        isPi = kTRUE;
+        
+        if (!hasTOF) {
+          fPIDtype = kV0idNoTOF;
+        }
+        else {
+          if (TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kPion)) < 3.0)
+            fPIDtype = kV0idPlusTOFaccepted;
+          else  
+            fPIDtype = kV0idPlusTOFrejected;
+        }
+      }
+      else { 
+        // Non-V0
+        isPr = kTRUE; // If particle is accepted, it is a proton (for pions, only V0s are used)
+        
+        if (fPtpc < 4.0 && //TODO was 2.7 // Do not accept non-V0s above this threshold -> High contamination!!!
+            (fDeDx >= 50. / TMath::Power(fPtpc, 1.3))) {// Pattern recognition instead of TPC cut to be ~independent of old TPC expected dEdx
+          if (fPtpc < 0.6) {
+            fPIDtype = kTPCid;
+          }
+          // fPtpc >= 0.6
+          else if (hasTOF && TMath::Abs(fPIDResponse->NumberOfSigmasTOF(track, AliPID::kProton)) < 3.0) {
+              fPIDtype = kTPCandTOFid;
+          }
+          else
+            continue; // Reject particle
+        }
+        else
+          continue; // Reject particle
+      }
+    }
+    
+    if (fDoAdditionalQA) {
+      Double_t tofTime = track->GetTOFsignal();//in ps
+      Double_t tof = tofTime * 1E-3; // ns, average T0 fill subtracted, no info from T0detector   
+      Double_t length2 = track->GetIntegratedLength();
+      Double_t c = TMath::C() * 1.E-9;// m/ns
+      length2 = length2 * 0.01; // in meters
+      tof = tof * c;
+      Double_t beta = length2 / tof;
+      
+      Double_t nSigmaTOF = hasTOF ? fPIDResponse->NumberOfSigmasTOF(track, AliPID::kProton) : 999;
+      
+      // p_vert, p_TPC, eta, nSigmaTOF, beta, multiplicity
+      Double_t entry[6] = { track->P(), fPtpc, track->Eta(), nSigmaTOF, beta, fMultiplicity };
+      fhTOFqa->Fill(entry);
+    }
+    
+    // Prepare entry for tree (some quantities have already been set)
+    // Turn eta correction off -> Important here is the pure spline value and the selection via cuts. The correction
+    // can be re-done manually, if needed. But it cannot be undone!
+    
+    if (isPr)
+      fDeDxExpected = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kProton, AliTPCPIDResponse::kdEdxDefault, 
+                                                                       fCorrectdEdxEtaDependence, fCorrectdEdxMultiplicityDependence);
+    else if (isPi)
+      fDeDxExpected = fPIDResponse->GetTPCResponse().GetExpectedSignal(track, AliPID::kPion, AliTPCPIDResponse::kdEdxDefault, 
+                                                                       fCorrectdEdxEtaDependence, fCorrectdEdxMultiplicityDependence);
+    else
+      fDeDxExpected = -1.;
+    fTanTheta = track->GetInnerParam()->GetTgl();
+    //fSinAlpha = track->GetInnerParam()->GetSnp();
+    //fY = track->GetInnerParam()->GetY();
+    fTPCsignalN = track->GetTPCsignalN();
+    
+    if (fStoreNumOfSubthresholdclusters) {
+      const AliTPCdEdxInfo *clsInfo = track->GetTPCdEdxInfo();
+      
+      if (clsInfo) {
+        Double32_t signal[4] = {0}; // 0: IROC only; 1: OROC short padas; 2: OROC long pads; 3:OROC combined
+        Char_t ncl[3] = {0};        // clusters above threshold; 0: IROC; 1: OROC short; 2: OROC long
+        Char_t nrows[3] = {0};      // clusters above and below threshold; 0: IROC; 1: OROC short; 2: OROC long
+
+        clsInfo->GetTPCSignalRegionInfo(signal, ncl, nrows);
+
+        fTPCsignalNsubthreshold = nrows[0] + nrows[1] + nrows[2] - ncl[0] - ncl[1] - ncl[2]; 
+      }
+      else
+        fTPCsignalNsubthreshold = 200;// Set to invalid value
+    }
+    
+    
+    if (fStoreNumClustersInActiveVolume) 
+      fNumTPCClustersInActiveVolume = track->GetLengthInActiveZone(1, 1.8, 220, magField);
+    else
+      fNumTPCClustersInActiveVolume = 200.;//Set to invalid value
+    
+    
+    /* START
+    const Double_t tanTheta = track->GetInnerParam()->GetTgl();
+
+    // Constant in formula for B in kGauss (factor 0.1 to convert B from Tesla to kGauss),
+    // pT in GeV/c (factor c*1e-9 to arrive at GeV/c) and curvature in 1/cm (factor 0.01 to get from m to cm)
+    const Double_t constant = TMath::C()* 1e-9 * 0.1 * 0.01;
+    const Double_t curvature = magField * constant / track->Pt(); // in 1./cm
+    
+    //const Double_t angleIROC = TMath::ASin(TMath::Min(TMath::Abs(par.GetPadRowRadii(0,  par.GetNRow(0)  / 2.) * curvature) * 0.5, 1.));
+    //const Double_t angleOROC = TMath::ASin(TMath::Min(TMath::Abs(par.GetPadRowRadii(36, par.GetNRow(36) / 2.) * curvature) * 0.5, 1.));
+    
+    Double_t averageddzdr = 0.;
+    Int_t nParts = 0;
+
+    for (Double_t r = 85; r < 245; r++) {
+      Double_t sinPhiLocal = TMath::Abs(r*curvature*0.5);
+      
+      // Cut on |sin(phi)| as during reco
+      if (TMath::Abs(sinPhiLocal) <= 0.95) {
+        const Double_t phiLocal = TMath::ASin(sinPhiLocal);
+        const Double_t tanPhiLocal = TMath::Tan(phiLocal);
+        
+        averageddzdr += tanTheta * TMath::Sqrt(1. + tanPhiLocal * tanPhiLocal); 
+        nParts++;
+      }
+    }
+    
+    if (nParts > 0)
+      averageddzdr /= nParts; 
+    else {
+      AliError("Problems during determination of dz/dr. Skipping track!");
+      continue;
+    }
+    
+
+    //printf("padRow 0 / 36: %f / %f\n", par.GetPadRowRadii(0,  par.GetNRow(0) / 2.), par.GetPadRowRadii(36, par.GetNRow(36) / 2.));
+    //printf("pT: %f\nFactor/magField(kGs)/curvature^-1: %f / %f /%f\nIROC/OROC/averageOld/average: %f / %f / %f / //%f\ntanThetaGlobalFromTheta/tanTheta/dzdr: %f / %f / %f\n\n",
+    //        track->Pt(), constant, magField, 1./curvature, angleIROC, angleOROC, angleAverage, averageAngle, TMath::Tan(-track->Theta() + TMath::Pi() / 2.0), tanTheta, dzdr);
+    //printf("pT: %f\nFactor/magField(kGs)/curvature^-1: %f / %f /%f\ntanThetaGlobalFromTheta/tanTheta/Averageddzdr: %f / %f / %f\n\n",
+    //        track->Pt(), constant, magField, 1./curvature, TMath::Tan(-track->Theta() + TMath::Pi() / 2.0), tanTheta, averageddzdr);
+
+  
+    fTanTheta = averageddzdr;
+    */
+    
+    if (isPr)
+      fTree->Fill();
+    else if (isPi)
+      fTreePions->Fill();
+  } //track loop 
+
+  // Post output data.
+  PostData(1, fTree);
+  PostData(2, fTreePions);
+
+  if (fDoAdditionalQA)
+    PostData(3, fOutputContainer);
+  
+  // Clear the V0 PID arrays
+  ClearV0PIDlist();
+}      
+
+//________________________________________________________________________
+void AliTPCPIDEtaTree::Terminate(const Option_t *)
+{
+  // Called once at the end of the query
+}
diff --git a/PWGPP/TPC/AliTPCPIDEtaTree.h b/PWGPP/TPC/AliTPCPIDEtaTree.h
new file mode 100644 (file)
index 0000000..25d8b9b
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef ALITPCPIDETATREE_H
+#define ALITPCPIDETATREE_H
+
+/*
+This task determines the eta dependence of the TPC signal.
+For this purpose, only tracks fulfilling some standard quality cuts are taken into account.
+The obtained data can be used to derive the functional behaviour of the eta dependence.
+Such a function can be plugged into this task to correct for the eta dependence and to see
+if there is then really no eta dependence left.
+
+Class written by Benjamin Hess.
+Contact: bhess@cern.ch
+*/
+
+class TTree;
+class TObjArray;
+class THnSparse;
+class TH2I;
+
+#include "AliTPCPIDBase.h"
+
+class AliTPCPIDEtaTree : public AliTPCPIDBase {
+ public:
+  enum PIDtype { kMCid = 0, kTPCid = 1, kV0idNoTOF = 2, kTPCandTOFid = 3, kV0idPlusTOFaccepted = 4, kV0idPlusTOFrejected = 5 };
+  
+  AliTPCPIDEtaTree();
+  AliTPCPIDEtaTree(const char *name);
+  virtual ~AliTPCPIDEtaTree();
+  
+  virtual void   UserCreateOutputObjects();
+  virtual void   UserExec(Option_t *option);
+  virtual void   Terminate(const Option_t*);
+  
+  Bool_t GetCorrectdEdxEtaDependence() const { return fCorrectdEdxEtaDependence; };
+  void SetCorrectdEdxEtaDependence(Bool_t flag) { fCorrectdEdxEtaDependence = flag; };
+  
+  Bool_t GetCorrectdEdxMultiplicityDependence() const { return fCorrectdEdxMultiplicityDependence; };
+  void SetCorrectdEdxMultiplicityDependence(Bool_t flag) { fCorrectdEdxMultiplicityDependence = flag; };
+  
+  Bool_t GetDoAdditionalQA() const { return fDoAdditionalQA; };
+  void SetDoAdditionalQA(Bool_t doAdditionalQA = kTRUE) { fDoAdditionalQA = doAdditionalQA; };
+  
+  Bool_t GetStoreMultiplicity() const  { return fStoreMultiplicity; };
+  void SetStoreMultiplicity(Bool_t storeMultiplicity = kTRUE) { fStoreMultiplicity = storeMultiplicity; };
+  
+  Bool_t GetStoreNumOfSubthresholdclusters() const  { return fStoreNumOfSubthresholdclusters; };
+  void SetStoreNumOfSubthresholdclusters(Bool_t storeNumOfSubthresholdclusters = kTRUE)
+    { fStoreNumOfSubthresholdclusters = storeNumOfSubthresholdclusters; };
+    
+  Bool_t GetStoreNumClustersInActiveVolume() const  { return fStoreNumClustersInActiveVolume; };
+  void SetStoreNumClustersInActiveVolume(Bool_t storeNumClustersInActiveVolume = kTRUE)
+    { fStoreNumClustersInActiveVolume = storeNumClustersInActiveVolume; };
+  
+  Double_t GetPtpcPionCut() const { return fPtpcPionCut; };
+  void SetPtpcPionCut(Double_t pTPCpionCut) { fPtpcPionCut = pTPCpionCut; };
+  
+ private:
+  Bool_t fStoreMultiplicity; // Store multiplicity in tree?
+  Bool_t fStoreNumOfSubthresholdclusters; // Store number of subthreshold clusters in tree?
+  Bool_t fStoreNumClustersInActiveVolume; // Store number of clusters in active volume in tree?
+  Bool_t fDoAdditionalQA; // Save output for additional QA, like TOF QA?
+  Double_t fPtpcPionCut; // Cut on pions with lower tpc momentum
+  
+  Double_t fPtpc; // TPC momentum
+  Double_t fPt; // Transverse momentum
+  Double_t fDeDx; // Measured dE/dx
+  Double_t fDeDxExpected; // Expected dE/dx according to parametrisation
+  Double_t fTanTheta; // Tangens of (local) theta at TPC inner wall
+  //Double_t fSinAlpha; // Sine of (local) phi at TPC inner wall
+  //Double_t fY; // Local Y at TPC inner wall
+  Double_t fPhiPrime; // Phi prime
+  UShort_t fTPCsignalN; // Number of TPC clusters for PID
+  UShort_t fTPCsignalNsubthreshold; // Number of TPC subthreshold clusters for PID
+  Double_t fNumTPCClustersInActiveVolume; // Number of TPC clusters in active volume
+  UChar_t  fPIDtype; // Type of identification (TPC dEdx, V0, ...)
+  
+  // In case of PbpB
+  Int_t fMultiplicity; // Multiplicity in case of PbPb
+  
+  Bool_t fCorrectdEdxEtaDependence;    // Correct eta dependence for dEdxExpected
+  Bool_t fCorrectdEdxMultiplicityDependence;    // Correct multiplicity dependence for dEdxExpected
+  
+  TTree* fTree; //! data tree
+  TTree* fTreePions; //! data tree pions
+  
+  TObjArray* fOutputContainer; //! Output data container for TOF qa
+  THnSparseI* fhTOFqa; //! THnSparse with TOF qa data
+  TH2I* fhMultiplicityQA; //! QA histo for multiplicity
+  
+  AliTPCPIDEtaTree(const AliTPCPIDEtaTree&); // not implemented
+  AliTPCPIDEtaTree& operator=(const AliTPCPIDEtaTree&); // not implemented
+  
+  ClassDef(AliTPCPIDEtaTree, 3); 
+};
+
+#endif
diff --git a/PWGPP/TPC/AliTPCcalibResidualPID.cxx b/PWGPP/TPC/AliTPCcalibResidualPID.cxx
new file mode 100644 (file)
index 0000000..82e1b39
--- /dev/null
@@ -0,0 +1,3700 @@
+/**************************************************************************
+* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
+*                                                                        *
+* Author: Yvonne Pachmayer <pachmay@physi.uni-heidelberg.de>             *
+* 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.                  *
+**************************************************************************/
+//
+// The task:
+// stores TPC PID quantities in a THnSparse
+// output can then be used for e.g. dEdx calibration
+//
+// Author:
+// Yvonne Pachmayer <pachmay@physi.uni-heidelberg.de>
+//
+
+
+#include "AliTPCcalibResidualPID.h"
+#include "TChain.h"
+#include "AliAnalysisManager.h"
+#include "AliESDEvent.h"
+#include "AliAODEvent.h"
+#include "AliMCEvent.h"
+#include "AliMCParticle.h"
+//#include "AliStack.h"
+#include "AliPID.h"
+#include "AliTPCcalibResidualPID.h"
+#include "AliESDtrack.h"
+#include "AliESDtrackCuts.h"
+#include "AliESDpid.h"
+#include "AliESDInputHandler.h"
+#include "AliESDv0KineCuts.h"
+#include "AliESDv0.h"
+#include "AliCentrality.h"
+#include "THnSparse.h"
+#include "TH2D.h"
+#include "TCanvas.h"
+#include "TGraphErrors.h"
+#include "TMultiGraph.h"
+#include "TAxis.h"
+#include "TFile.h"
+#include "TSpline.h"
+#include "TStyle.h"
+#include "AliTPCdEdxInfo.h"
+#include "TString.h"
+#include "TFitResult.h"
+#include "TH1F.h"
+#include "TLegend.h"
+#include "TVirtualFitter.h"
+
+using namespace std;
+
+ClassImp(AliTPCcalibResidualPID)
+
+Double_t AliTPCcalibResidualPID::fgCutGeo = 1.;   
+Double_t AliTPCcalibResidualPID::fgCutNcr = 0.85; 
+Double_t AliTPCcalibResidualPID::fgCutNcl = 0.7;  
+
+//________________________________________________________________________
+AliTPCcalibResidualPID::AliTPCcalibResidualPID()
+  : AliAnalysisTaskSE(), fESD(0), fMC(0), fOutputContainer(0), fESDtrackCuts(0), fESDtrackCutsV0(0), fESDpid(0),
+    fUseTPCCutMIGeo(kFALSE),
+    fUseMCinfo(kTRUE),
+    fIsPbpOrpPb(kFALSE),
+    fZvtxCutEvent(9999.0),
+    fV0KineCuts(0x0),
+    fNumTagsStored(0),
+    fV0tags(0x0),
+    fV0motherIndex(0x0),
+    fV0motherPDG(0x0),
+    fProduceAllPadTypes(0),
+    fProduceGlobal(0),
+    fProduceShortPads(0),
+    fProduceMediumPads(0),
+    fProduceLongPads(0),
+    fProduceOroc(0),
+    fHistPidQA(0), 
+    fHistPidQAshort(0),
+    fHistPidQAmedium(0),
+    fHistPidQAlong(0),
+    fHistPidQAoroc(0),
+    fProduceTPCSignalSparse(0),
+    fCorrectdEdxEtaDependence(0),
+    fCorrectdEdxMultiplicityDependence(0),
+    fThnspTpc(0),
+    fQAList(0x0),
+    fhInvMassGamma(0x0),
+    fhInvMassK0s(0x0),
+    fhInvMassLambda(0x0),
+    fhInvMassAntiLambda(0x0),
+    fhArmenterosAll(0x0),
+    fhArmenterosGamma(0x0),
+    fhArmenterosK0s(0x0),
+    fhArmenterosLambda(0x0),
+    fhArmenterosAntiLambda(0x0),
+    fHistSharedClusQAV0Pi(0x0),
+    fHistSharedClusQAV0Pr(0x0),
+    fHistSharedClusQAV0El(0x0)
+{
+  // default Constructor
+   /* fast compilation test
+     gSystem->Load("libANALYSIS");
+     gSystem->Load("libANALYSISalice");
+     .L /lustre/alice/akalweit/train/trunk/util/statsQA/AliTPCcalibResidualPID.cxx++
+   */
+
+}
+
+
+//________________________________________________________________________
+AliTPCcalibResidualPID::AliTPCcalibResidualPID(const char *name)
+  : AliAnalysisTaskSE(name), fESD(0), fMC(0), fOutputContainer(0), fESDtrackCuts(0), fESDtrackCutsV0(0), fESDpid(0),
+    fUseTPCCutMIGeo(kFALSE),
+    fUseMCinfo(kTRUE),
+    fIsPbpOrpPb(kFALSE),
+    fZvtxCutEvent(9999.0),
+    fV0KineCuts(0x0),
+    fNumTagsStored(0),
+    fV0tags(0x0),
+    fV0motherIndex(0x0),
+    fV0motherPDG(0x0),
+    fProduceAllPadTypes(0),
+    fProduceGlobal(0),
+    fProduceShortPads(0),
+    fProduceMediumPads(0),
+    fProduceLongPads(0),
+    fProduceOroc(0),
+    fHistPidQA(0),
+    fHistPidQAshort(0),
+    fHistPidQAmedium(0),
+    fHistPidQAlong(0), 
+    fHistPidQAoroc(0),
+    fProduceTPCSignalSparse(0),
+    fCorrectdEdxEtaDependence(0),
+    fCorrectdEdxMultiplicityDependence(0),
+    fThnspTpc(0),
+    fQAList(0x0),
+    fhInvMassGamma(0x0),
+    fhInvMassK0s(0x0),
+    fhInvMassLambda(0x0),
+    fhInvMassAntiLambda(0x0),
+    fhArmenterosAll(0x0),
+    fhArmenterosGamma(0x0),
+    fhArmenterosK0s(0x0),
+    fhArmenterosLambda(0x0),
+    fhArmenterosAntiLambda(0x0),
+    fHistSharedClusQAV0Pi(0x0),
+    fHistSharedClusQAV0Pr(0x0),
+    fHistSharedClusQAV0El(0x0)
+{
+
+  //fESDtrackCuts = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+  //fESDtrackCutsV0 = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kFALSE);
+
+  // Constructor
+  DefineInput(0, TChain::Class());
+  DefineOutput(1, TObjArray::Class());
+  DefineOutput(2, TObjArray::Class());
+
+}
+
+
+//_________________________________________________
+AliTPCcalibResidualPID::~AliTPCcalibResidualPID()
+{
+  delete fOutputContainer;
+  fOutputContainer = 0;
+  
+  delete fQAList;
+  fQAList = 0;
+  
+  delete fESDtrackCuts;
+  fESDtrackCuts = 0;
+  
+  delete fESDtrackCutsV0;
+  fESDtrackCutsV0 = 0;
+  
+  delete fV0KineCuts;
+  fV0KineCuts = 0;
+  
+  delete fV0tags;
+  fV0tags = 0;
+  fNumTagsStored = 0;
+  
+  delete fV0motherIndex;
+  fV0motherIndex = 0;
+  
+  delete fV0motherPDG;
+  fV0motherPDG = 0;
+}
+
+
+//________________________________________________________________________
+void AliTPCcalibResidualPID::UserCreateOutputObjects()
+{
+    
+    // THnSparse binning
+    const Int_t kNdim = 9;
+    //v0id,  dEdx,  TPCsigele,  TPCsigpion,  TOFbit,  eta, TPCclus, centr, p
+    Int_t bins[kNdim] =    {    4,    250,    200,    200,          8,    20,        50,   20,   100};
+    Double_t xmin[kNdim] = {  0.5,     30,   -10.,   -10.,       -1.5,   -1.,       60.,   0.,   0.1};
+    Double_t xmax[kNdim] = {  4.5,   500.,    10.,    10.,        6.5,    1.,      160.,  100,   4};
+    fThnspTpc= new THnSparseF("tpcsignals", "TPC signal;v0id;tpc signal;tpcsigele;tpcsigpion;tofbit;eta;tpcclus;centr;p (GeV/c)", kNdim, bins, xmin, xmax);
+    BinLogAxis(fThnspTpc, 8);
+
+    //
+    // 0.ptot, 1.tpcSig, 2.particle ID, 3. assumed particle, 4. nSigmaTPC (4x), 5. nSigmaTOF (4x), 6. centrality
+    // Concerning 2 (particle ID): 
+    // - in case of MC: Contains MC ID. Bin 1-4 (<=> Slot 0-3): el, pi, ka, pr
+    // - in case of data: Contains V0 particles + bin with all others: Bin 1-4 (<=> Slot 0-3): All non-V0s, V0-el, V0-pi, V0-pr
+    //
+        
+    Int_t    binsHistQA[7] = {135, 1980,    4,    5, 40, 10,   40};
+    Double_t xminHistQA[7] = {0.1,   20, -0.5, -0.5, -10, -5,   0.};
+    Double_t xmaxHistQA[7] = {50., 2000,  3.5,  4.5,  10,  5, 20000};
+    fHistPidQA = new THnSparseF("fHistPidQA","PID QA",7,binsHistQA,xminHistQA,xmaxHistQA);
+    BinLogAxis(fHistPidQA, 0);
+
+    //
+    fHistPidQAshort  = new THnSparseF("fHistPidQAshort" ,"PID QA -- short pads",7,binsHistQA,xminHistQA,xmaxHistQA);
+    fHistPidQAmedium = new THnSparseF("fHistPidQAmedium","PID QA -- med pads",7,binsHistQA,xminHistQA,xmaxHistQA);
+    fHistPidQAlong   = new THnSparseF("fHistPidQAlong"  ,"PID QA -- long pads",7,binsHistQA,xminHistQA,xmaxHistQA);
+    fHistPidQAoroc   = new THnSparseF("fHistPidQAoroc"  ,"PID QA -- oroc",7,binsHistQA,xminHistQA,xmaxHistQA);
+    BinLogAxis(fHistPidQAshort, 0);
+    BinLogAxis(fHistPidQAmedium, 0);
+    BinLogAxis(fHistPidQAlong, 0);
+    BinLogAxis(fHistPidQAoroc, 0);
+    //
+    fOutputContainer = new TObjArray(2);
+    fOutputContainer->SetName(GetName());
+    fOutputContainer->SetOwner();
+
+    if(fProduceTPCSignalSparse)fOutputContainer->Add(fThnspTpc);
+    if(fProduceGlobal)fOutputContainer->Add(fHistPidQA);
+    if(fProduceAllPadTypes || fProduceShortPads)fOutputContainer->Add(fHistPidQAshort);
+    if(fProduceAllPadTypes || fProduceMediumPads)fOutputContainer->Add(fHistPidQAmedium);
+    if(fProduceAllPadTypes || fProduceLongPads)fOutputContainer->Add(fHistPidQAlong);
+    if(fProduceAllPadTypes || fProduceOroc)fOutputContainer->Add(fHistPidQAoroc);
+    
+    // V0 Kine cuts 
+    fV0KineCuts = new AliESDv0KineCuts;
+    fV0KineCuts->SetGammaCutChi2NDF(5.);
+    // Only accept V0el with prod. radius within 45 cm -> PID will by systematically biased for larger values!
+    Float_t gammaProdVertexRadiusCuts[2] = { 3.0, 45. }; 
+    fV0KineCuts->SetGammaCutVertexR(&gammaProdVertexRadiusCuts[0]);
+    
+    
+    fQAList = new TObjArray(4);
+    fQAList->SetName(GetName());
+    fQAList->SetOwner();
+    
+    fhInvMassGamma      = new TH1F("fhInvMassGamma", "Invariant Mass of gammas; m_{ee} (GeV/#it{c}^{2}); Entries", 200, 0., 0.2);
+    fhInvMassK0s        = new TH1F("fhInvMassK0s", "Invariant Mass of K0s; m_{#pi#pi} (GeV/#it{c}^{2}); Entries;", 200, 0.45, 0.55);
+    fhInvMassLambda     = new TH1F("fhInvMassLambda", "Invariant Mass of lambdas; m_{p#pi^{-}} (GeV/#it{c}^{2}); Entries", 200, 1.05, 1.15);
+    fhInvMassAntiLambda = new TH1F("fhInvMassAntiLambda", "Invariant Mass of anti-lambdas; m_{#pi^{+}#bar{p}} (GeV/#it{c}^{2}); Entries",
+                                   200, 1.05, 1.15);
+    
+    fQAList->Add(fhInvMassGamma);
+    fQAList->Add(fhInvMassK0s);
+    fQAList->Add(fhInvMassLambda);
+    fQAList->Add(fhInvMassAntiLambda);
+    
+    
+    fhArmenterosAll = new TH2F("fhArmenterosAll",
+                               "Armenteros plot all V0s;#alpha = (#it{p}^{+}_{L}-#it{p}^{-}_{L})/(#it{p}^{+}_{L}+#it{p}^{-}_{L});#it{q}_{T} (GeV/#it{c})",
+                               200, -1., 1., 200, 0., 0.4);
+    fhArmenterosGamma = new TH2F("fhArmenterosGamma",
+                                 "Armenteros plot Gamma;#alpha = (#it{p}^{+}_{L}-#it{p}^{-}_{L})/(#it{p}^{+}_{L}+#it{p}^{-}_{L});#it{q}_{T} (GeV/#it{c})",
+                                 200, -1., 1., 200, 0., 0.4);
+    fhArmenterosK0s = new TH2F("fhArmenterosK0s",
+                               "Armenteros plot K0s;#alpha = (#it{p}^{+}_{L}-#it{p}^{-}_{L})/(#it{p}^{+}_{L}+#it{p}^{-}_{L});#it{q}_{T} (GeV/#it{c})",
+                               200, -1., 1., 200, 0., 0.4);
+    fhArmenterosLambda = new TH2F("fhArmenterosLambda",
+                                 "Armenteros plot lambda;#alpha = (#it{p}^{+}_{L}-#it{p}^{-}_{L})/(#it{p}^{+}_{L}+#it{p}^{-}_{L});#it{q}_{T} (GeV/#it{c})",
+                                 200, -1., 1., 200, 0., 0.4);
+    fhArmenterosAntiLambda = new TH2F("fhArmenterosAntiLambda",
+                                      "Armenteros plot anti-lambda;#alpha = (#it{p}^{+}_{L}-#it{p}^{-}_{L})/(#it{p}^{+}_{L}+#it{p}^{-}_{L});#it{q}_{T} (GeV/#it{c})",
+                                      200, -1., 1., 200, 0., 0.4);
+
+    fQAList->Add(fhArmenterosAll);
+    fQAList->Add(fhArmenterosGamma);
+    fQAList->Add(fhArmenterosK0s);
+    fQAList->Add(fhArmenterosLambda);
+    fQAList->Add(fhArmenterosAntiLambda);
+    
+    
+    const Int_t dimQASharedClusters = 4;
+    const TString axisTitles[dimQASharedClusters] = { "#it{p}_{TPC} (GeV/#it{c})", "#it{#Delta'}", "#it{N}_{shared cl}", "Pad row"};
+    Int_t    binsHistQASharedClusters[dimQASharedClusters] = { 100,  100, 160, 160};
+    Double_t xminHistQASharedClusters[dimQASharedClusters] = { 0.3, 0.5,  0,   -1};
+    Double_t xmaxHistQASharedClusters[dimQASharedClusters] = { 20,  1.5, 160, 159};
+    
+    fHistSharedClusQAV0Pi = new THnSparseF("fHistSharedClusQAV0Pi" ,"PID QA shared clusters - V0 pi", dimQASharedClusters,
+                                           binsHistQASharedClusters, xminHistQASharedClusters, xmaxHistQASharedClusters);
+    BinLogAxis(fHistSharedClusQAV0Pi, 0);
+    for (Int_t i = 0; i < dimQASharedClusters; i++)
+      fHistSharedClusQAV0Pi->GetAxis(i)->SetTitle(axisTitles[i].Data());
+    fQAList->Add(fHistSharedClusQAV0Pi);
+    
+    fHistSharedClusQAV0Pr = new THnSparseF("fHistSharedClusQAV0Pr" ,"PID QA shared clusters - V0 pr", dimQASharedClusters,
+                                           binsHistQASharedClusters, xminHistQASharedClusters, xmaxHistQASharedClusters);
+    BinLogAxis(fHistSharedClusQAV0Pr, 0);
+    for (Int_t i = 0; i < dimQASharedClusters; i++)
+      fHistSharedClusQAV0Pi->GetAxis(i)->SetTitle(axisTitles[i].Data());
+    fQAList->Add(fHistSharedClusQAV0Pr);
+    fHistSharedClusQAV0El = new THnSparseF("fHistSharedClusQAV0El" ,"PID QA shared clusters - V0 el", dimQASharedClusters,
+                                           binsHistQASharedClusters, xminHistQASharedClusters, xmaxHistQASharedClusters);
+    BinLogAxis(fHistSharedClusQAV0El, 0);
+    for (Int_t i = 0; i < dimQASharedClusters; i++)
+      fHistSharedClusQAV0Pi->GetAxis(i)->SetTitle(axisTitles[i].Data());
+    fQAList->Add(fHistSharedClusQAV0El);
+
+    PostData(1,fOutputContainer);
+    PostData(2,fQAList);
+}
+
+
+//_____________________________________________________________________________
+void AliTPCcalibResidualPID::UserExec(Option_t *)
+{
+    //calls the Process function
+    AliESDInputHandler *esdH = dynamic_cast<AliESDInputHandler*> (AliAnalysisManager::GetAnalysisManager()->GetInputEventHandler());
+
+    if (!esdH) {
+      printf("ERROR: Could not get ESDInputHandler \n");
+    }
+    else fESD = esdH->GetEvent();
+    
+    // If MC, forward MCevent
+    fMC = dynamic_cast<AliMCEvent*>(MCEvent());
+    //
+    Process(fESD, fMC);
+    //
+    PostData(1,fOutputContainer);
+    PostData(2,fQAList);
+}
+
+
+
+//________________________________________________________________________
+void AliTPCcalibResidualPID::Process(AliESDEvent *const esdEvent, AliMCEvent *const mcEvent)
+{
+  
+  //called for each event
+  if (!esdEvent) {
+    Printf("ERROR: esdEvent not available"); 
+    return;
+  }
+
+  if(!((AliESDInputHandler*)(AliAnalysisManager::GetAnalysisManager()->GetInputEventHandler()))) printf("ESD inputhandler not available \n");
+
+  if (!fESDpid) {
+    fESDpid = ((AliESDInputHandler*)(AliAnalysisManager::GetAnalysisManager()->GetInputEventHandler()))->GetESDpid();
+  }
+
+  if (!fESDpid || !fV0KineCuts)
+    return;
+
+
+  Float_t centralityFper=99;
+
+  AliCentrality *esdCentrality = esdEvent->GetCentrality();
+  centralityFper = esdCentrality->GetCentralityPercentile("V0M");
+
+  if (!GetVertexIsOk(esdEvent))
+    return;
+  
+  const AliESDVertex* fESDvertex = esdEvent->GetPrimaryVertexTracks(); 
+  if (!fESDvertex)
+    return;
+  
+  Int_t ncontr = fESDvertex->GetNContributors();
+  
+  if (ncontr <= 0)
+    return;
+
+  // Fill V0 arrays with V0s
+  FillV0PIDlist(esdEvent);
+  
+  // Array with flags wheter QA for this V0 was already done or not
+  const Int_t numV0s = esdEvent->GetNumberOfV0s();
+  Bool_t v0QAadded[numV0s];
+  for (Int_t i = 0; i < numV0s; i++)
+    v0QAadded[i] = kFALSE;
+  
+  Int_t nTotTracks = esdEvent->GetNumberOfTracks();
+  for (Int_t iTracks = 0; iTracks < nTotTracks; iTracks++){//begin track loop 
+    AliESDtrack *trackESD = esdEvent->GetTrack(iTracks);
+    if(!trackESD) {
+      Printf("ERROR: Could not receive track %d (esd loop)", iTracks);
+      continue;
+    }
+    if((TMath::Abs(trackESD->Eta())) > 0.9)
+      continue;
+    
+     // Do not distinguish positively and negatively charged V0's
+    Char_t v0tagAllCharges = TMath::Abs(GetV0tag(iTracks));
+    if (v0tagAllCharges == -99) {
+      AliError(Form("Problem with V0 tag list (requested V0 track for track %d from %d (list status %d))!", iTracks, esdEvent->GetNumberOfTracks(),
+                    fV0tags != 0x0));
+      continue;
+    }
+    
+    Bool_t isV0el = v0tagAllCharges == 14;
+    Bool_t isV0pi = v0tagAllCharges == 15;
+    Bool_t isV0pr = v0tagAllCharges == 16;
+    Bool_t isV0 = isV0el || isV0pi || isV0pr;
+    
+    if (mcEvent && fUseMCinfo) {
+      // For MC, do not look for V0s, i.e. demand the non-V0 track cuts
+      if (fESDtrackCuts && !fESDtrackCuts->AcceptTrack(trackESD)) continue;
+      
+      if (fUseTPCCutMIGeo) {
+        // If cut on geometry is active, don't cut on number of clusters, since such a cut is already included.
+        if (!TPCCutMIGeo(trackESD, esdEvent))
+          continue;
+      }
+      else {
+        // If cut on geometry is not active, always cut on num clusters
+        if (trackESD->GetTPCsignalN() < 60)
+          continue;
+      }
+    }
+    else {
+      // For data, take V0 AND non-V0 with separate cuts
+      //if (!isV0 && fESDtrackCuts && !fESDtrackCuts->AcceptTrack(trackESD)) continue;
+      if (isV0) {
+        if (fESDtrackCutsV0 && !fESDtrackCutsV0->AcceptTrack(trackESD))
+          continue;
+      }
+      else {
+        if (fESDtrackCuts && !fESDtrackCuts->AcceptTrack(trackESD))
+          continue;
+      }
+        
+      if (fUseTPCCutMIGeo) {
+        // If cut on geometry is active, don't cut on number of clusters, since such a cut is already included.
+        if (!isV0) {
+          if (!TPCCutMIGeo(trackESD, esdEvent))
+            continue;
+        }
+        else {
+          // One should not cut on geometry for V0's since they have different topology. (Loosely) cut on num of clusters instead.
+          if (trackESD->GetTPCsignalN() < 60)
+            continue;
+        }
+      }
+      else {
+        // If cut on geometry is not active, always cut on num clusters
+        if (trackESD->GetTPCsignalN() < 60)
+          continue;
+      }
+    }
+
+    const AliExternalTrackParam *paramIn = trackESD->GetInnerParam();
+    Float_t precin=-1;
+    if(paramIn) {
+      precin=paramIn->GetP();
+    } else {
+      continue;
+    }
+    Int_t precdefault=CompareFloat(precin,-1);
+    if(precdefault==1) continue; // momentum cut
+    //
+    
+    AliMCParticle* mcTrack = 0x0;
+    Int_t particleID = -1;
+    Int_t v0id = 0;
+    
+    if (mcEvent && fUseMCinfo) {
+      // MC - particle ID = MC ID
+      Int_t label = trackESD->GetLabel();
+      
+      if (label < 0)
+        continue; // If MC is available, reject tracks with negative ESD label
+        
+      mcTrack = dynamic_cast<AliMCParticle*>(mcEvent->GetTrack(TMath::Abs(label)));
+      if (!mcTrack) {
+        Printf("ERROR: Could not receive mcTrack with label %d for track %d", label, iTracks);
+        continue;
+      }
+      
+      /*// Only accept MC primaries
+      if (!mcEvent->Stack()->IsPhysicalPrimary(TMath::Abs(label))) {
+        continue;
+      }*/
+      
+      Int_t pdgAbs = TMath::Abs(mcTrack->PdgCode());
+      
+      if (pdgAbs == 11) { // electron
+        particleID = 0;
+      }
+      else if (pdgAbs == 211) { // pion
+        particleID = 1;
+      }
+      else if (pdgAbs == 321) { // kaon
+        particleID = 2;
+      }
+      else if (pdgAbs == 2212) { // proton
+        particleID = 3;
+      }
+      else
+        continue; // Reject all other particles
+    }
+    else {
+      // Data - particle ID = V0 ID (if V0)
+      if (isV0pi) { // pion
+        particleID = 2;
+        v0id = 2;
+      }
+      else if (isV0el) { // electron
+        particleID = 1;
+        v0id = 1;
+      }
+      else if (isV0pr) { // proton
+        particleID = 3;
+        v0id = 3;
+      }
+      else { // No V0-ID available (species must be determined via TPC, TOF, ...)
+        particleID = 0;
+      }
+    }
+
+
+    Float_t tpcsignal=trackESD->GetTPCsignal();
+    Int_t tofbit=-1;
+    Int_t iTOFpid=0;
+    Int_t ikTIME=0;
+    Double_t tpcnsigmaele=-10;
+    Double_t tpcnsigmapion=-10;
+    //
+    if ((trackESD->GetStatus() & AliESDtrack::kTOFpid)) iTOFpid = 1;
+    if ((trackESD->GetStatus() & AliESDtrack::kTIME)) ikTIME = 1;
+    Float_t time0 = fESDpid->GetTOFResponse().GetTimeZero();
+    //
+    if((iTOFpid==1) &&(ikTIME==1)){
+      Double_t tofnsigmaele= fESDpid->NumberOfSigmasTOF(trackESD,AliPID::kElectron, time0);
+      Double_t tofnsigmapion=fESDpid->NumberOfSigmasTOF(trackESD,AliPID::kPion, time0);
+      if (TMath::Abs(tofnsigmapion)<3) tofbit = 1;
+      if (TMath::Abs(tofnsigmaele)<3)  tofbit = 0;
+      tpcnsigmaele=fESDpid->NumberOfSigmasTPC(trackESD,AliPID::kElectron);
+      tpcnsigmapion=fESDpid->NumberOfSigmasTPC(trackESD,AliPID::kPion);
+    }
+    //
+    Int_t tpcnclusN=trackESD->GetTPCsignalN();
+    Double_t eta=trackESD->Eta();
+    
+    //
+    if(fProduceTPCSignalSparse){
+      Double_t contentSignal[9];
+      contentSignal[0]=v0id;
+      contentSignal[1]=tpcsignal;
+      contentSignal[2]=tpcnsigmaele;
+      contentSignal[3]=tpcnsigmapion;
+      contentSignal[4]=tofbit;
+      contentSignal[5]=eta;
+      contentSignal[6]=tpcnclusN;
+      contentSignal[7]=centralityFper;
+      contentSignal[8]=precin;
+      //
+      if (fThnspTpc->GetEntries() < 1e6) fThnspTpc->Fill(contentSignal);
+    }//should it be created or not
+
+
+
+    //
+    // 2nd THnSparse
+    //
+    Double_t tpcQA[5] = {fESDpid->NumberOfSigmasTPC(trackESD, AliPID::kElectron),
+                        fESDpid->NumberOfSigmasTPC(trackESD, AliPID::kPion),
+                        fESDpid->NumberOfSigmasTPC(trackESD, AliPID::kKaon),
+                        fESDpid->NumberOfSigmasTPC(trackESD, AliPID::kProton),
+                        0};
+    Double_t tofQA[5] = {fESDpid->NumberOfSigmasTOF(trackESD, AliPID::kElectron, time0),
+                        fESDpid->NumberOfSigmasTOF(trackESD, AliPID::kPion, time0),
+                        fESDpid->NumberOfSigmasTOF(trackESD, AliPID::kKaon, time0),
+                        fESDpid->NumberOfSigmasTOF(trackESD, AliPID::kProton, time0),
+                        0 };
+       
+    // id 5 is just again Kaons in restricted eta range
+    tpcQA[4] = tpcQA[2];
+    tofQA[4] = tofQA[2];
+    
+    //
+    // dE/dx in different pad regions
+    //
+    AliTPCdEdxInfo * infoTpcPid = trackESD->GetTPCdEdxInfo();
+    Double32_t signal[4]; Char_t ncl[3]; Char_t nrows[3];
+    if (infoTpcPid) {
+      infoTpcPid->GetTPCSignalRegionInfo(signal, ncl, nrows);
+    } else {
+      for(Int_t iarr = 0; iarr < 3; iarr++) {
+       signal[iarr] = 0;
+       ncl[iarr] = 0;
+       nrows[iarr] = 0;
+      }
+      signal[3] = 0;
+    }
+    //
+    UInt_t status = trackESD->GetStatus();
+    Bool_t hasTOFout  = status&AliESDtrack::kTOFout; 
+    Bool_t hasTOFtime = status&AliESDtrack::kTIME;
+    Bool_t hasTOF     = kFALSE;
+    if (hasTOFout && hasTOFtime) hasTOF = kTRUE;
+    Float_t length = trackESD->GetIntegratedLength();
+    // Length check only for primaries!
+    if (length < 350 && !isV0) hasTOF = kFALSE;
+    //
+    
+    if (!hasTOF) {
+      // Make sure that number of sigmas is large in this case, so that the track will be rejected if a TOF cut is applied
+      for (Int_t i = 0; i < 5; i++) {
+        tofQA[i] = 999;
+      }
+    }
+
+
+    Double_t processedTPCsignal[5] = { tpcsignal, tpcsignal, tpcsignal, tpcsignal, tpcsignal };
+  
+    
+    if (fCorrectdEdxEtaDependence && fCorrectdEdxMultiplicityDependence) {
+      processedTPCsignal[0] = fESDpid->GetTPCResponse().GetEtaAndMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kElectron,
+                                                                                                AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[1] = fESDpid->GetTPCResponse().GetEtaAndMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kPion,
+                                                                                                AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[2] = fESDpid->GetTPCResponse().GetEtaAndMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kKaon,
+                                                                                                AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[3] = fESDpid->GetTPCResponse().GetEtaAndMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kProton,
+                                                                                                AliTPCPIDResponse::kdEdxDefault);
+    }
+    else if (fCorrectdEdxEtaDependence) {
+      processedTPCsignal[0] = fESDpid->GetTPCResponse().GetEtaCorrectedTrackdEdx(trackESD, AliPID::kElectron, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[1] = fESDpid->GetTPCResponse().GetEtaCorrectedTrackdEdx(trackESD, AliPID::kPion, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[2] = fESDpid->GetTPCResponse().GetEtaCorrectedTrackdEdx(trackESD, AliPID::kKaon, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[3] = fESDpid->GetTPCResponse().GetEtaCorrectedTrackdEdx(trackESD, AliPID::kProton, AliTPCPIDResponse::kdEdxDefault);
+    }
+    else if (fCorrectdEdxMultiplicityDependence) {
+      processedTPCsignal[0] = fESDpid->GetTPCResponse().GetMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kElectron, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[1] = fESDpid->GetTPCResponse().GetMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kPion, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[2] = fESDpid->GetTPCResponse().GetMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kKaon, AliTPCPIDResponse::kdEdxDefault);
+      processedTPCsignal[3] = fESDpid->GetTPCResponse().GetMultiplicityCorrectedTrackdEdx(trackESD, AliPID::kProton, AliTPCPIDResponse::kdEdxDefault);
+    }
+    
+    // id 5 is just again Kaons in restricted eta range
+    processedTPCsignal[4] = processedTPCsignal[2];
+    
+    for(Int_t iPart = 0; iPart < 5; iPart++) {
+      // Only accept "Kaons" within |eta| < 0.2 for index 4 in case of data (no contamination in case of MC, so this index is not used)
+      if (iPart == 4 && ((mcEvent && fUseMCinfo) || abs(trackESD->Eta()) > 0.2))
+        continue;
+      
+      Double_t vecHistQA[7] = {precin, processedTPCsignal[iPart], particleID, iPart, tpcQA[iPart], tofQA[iPart], nTotTracks};
+      if (fProduceGlobal) fHistPidQA->Fill(vecHistQA);
+      vecHistQA[1] = signal[0]; vecHistQA[4] = ncl[0];
+      if ((fProduceAllPadTypes || fProduceShortPads)) fHistPidQAshort->Fill(vecHistQA);
+      //
+      vecHistQA[1] = signal[1]; vecHistQA[4] = ncl[1];
+      if ((fProduceAllPadTypes || fProduceMediumPads)) fHistPidQAmedium->Fill(vecHistQA);
+      //
+      vecHistQA[1] = signal[2]; vecHistQA[4] = ncl[2];
+      if ((fProduceAllPadTypes || fProduceLongPads)) fHistPidQAlong->Fill(vecHistQA);
+      //
+      vecHistQA[1] = signal[3]; vecHistQA[4] = nrows[1] + nrows[2];
+      if ((fProduceAllPadTypes || fProduceOroc)) fHistPidQAoroc->Fill(vecHistQA);      
+    }
+    
+    if (isV0) {
+      // Fill QA hists for shared clusters dE/dx
+      Double_t vecHistQA[4] = { precin, -1, trackESD->GetTPCSharedMap().CountBits(), -1 };
+      THnSparse* currHist = fHistSharedClusQAV0Pi;
+      
+      if (isV0pi) {
+        Double_t expectedDeDx = fESDpid->GetTPCResponse().GetExpectedSignal(trackESD, AliPID::kPion, AliTPCPIDResponse::kdEdxDefault,
+                                                                            fCorrectdEdxEtaDependence, fCorrectdEdxMultiplicityDependence); 
+        if (expectedDeDx > 0 ) {
+          vecHistQA[1] = tpcsignal / expectedDeDx;
+          currHist = fHistSharedClusQAV0Pi;
+        }
+      }
+      else if (isV0pr) {
+        Double_t expectedDeDx = fESDpid->GetTPCResponse().GetExpectedSignal(trackESD, AliPID::kProton, AliTPCPIDResponse::kdEdxDefault,
+                                                                            fCorrectdEdxEtaDependence, fCorrectdEdxMultiplicityDependence); 
+        if (expectedDeDx > 0 ) {
+          vecHistQA[1] = tpcsignal / expectedDeDx;
+          currHist = fHistSharedClusQAV0Pr;
+        }
+      }
+      else if (isV0el) {
+        Double_t expectedDeDx = fESDpid->GetTPCResponse().GetExpectedSignal(trackESD, AliPID::kElectron, AliTPCPIDResponse::kdEdxDefault,
+                                                                            fCorrectdEdxEtaDependence, fCorrectdEdxMultiplicityDependence); 
+        if (expectedDeDx > 0 ) {
+          vecHistQA[1] = tpcsignal / expectedDeDx;
+          currHist = fHistSharedClusQAV0El;
+        }
+      }
+      
+      Int_t iRowInd = -1;
+      // iRowInd == -1 for "all rows w/o multiple counting"
+      currHist->Fill(vecHistQA);
+      
+      // Fill hist for every pad row with shared cluster
+      for (iRowInd = 0; iRowInd < 159; iRowInd++) {
+        if (trackESD->GetTPCSharedMap().TestBitNumber(iRowInd)) {
+          vecHistQA[3] = iRowInd;
+          currHist->Fill(vecHistQA);
+        }
+      }
+      
+      
+      
+      // Check, whether the QA for this V0 mother was already filled into the histograms (is the case if the other daughter has already
+      // been processed). If not, set flag to kTRUE and fill the QA.
+      const Int_t iV0 = GetV0motherIndex(iTracks);
+      if (!v0QAadded[iV0]) {
+        v0QAadded[iV0] = kTRUE;
+      
+        AliESDv0* esdV0 = (AliESDv0*)esdEvent->GetV0(iV0);
+        
+        if (!esdV0) {
+          printf("Error: V0 tagged, but does not exist!\n");
+          continue;
+        }
+      
+        Float_t armVar[2] = {0.0,0.0};
+        fV0KineCuts->Armenteros(esdV0, armVar);
+        
+        const Int_t motherPDG = GetV0motherPDG(iTracks);
+        
+        // Mother and daughter match the requirements, otherwise it wouldn't be tagged as a V0. Now just fill the QA histos.
+        fhArmenterosAll->Fill(armVar[0], armVar[1]);
+        //if (esdV0->TestBit(BIT(14))) {
+        if (TMath::Abs(motherPDG) == 22) {
+          fhInvMassGamma->Fill(esdV0->GetEffMass(AliPID::kElectron, AliPID::kElectron));
+          fhArmenterosGamma->Fill(armVar[0], armVar[1]);
+        }
+        else if (TMath::Abs(motherPDG) == 310) {
+        //else if (esdV0->TestBit(BIT(15))) {
+          fhInvMassK0s->Fill(esdV0->GetEffMass(AliPID::kPion, AliPID::kPion));
+          fhArmenterosK0s->Fill(armVar[0], armVar[1]);
+        }
+        else if (motherPDG == 3122) {
+        //else if (esdV0->TestBit(BIT(16))) {
+          fhInvMassLambda->Fill(esdV0->GetEffMass(AliPID::kProton, AliPID::kPion));
+          fhArmenterosLambda->Fill(armVar[0], armVar[1]);
+        }
+        else if (motherPDG == -3122) {
+        //else if (esdV0->TestBit(BIT(17))) {
+          fhInvMassAntiLambda->Fill(esdV0->GetEffMass(AliPID::kPion, AliPID::kProton));
+          fhArmenterosAntiLambda->Fill(armVar[0], armVar[1]);
+        }
+      }
+    }
+  } //end track loop 
+  
+
+  // Clear the V0 PID arrays
+  ClearV0PIDlist();
+}      
+
+
+//________________________________________________________________________
+Int_t  AliTPCcalibResidualPID::CompareFloat(Float_t f1, Float_t f2) const
+{
+    //compares if the Float_t f1 is equal with f2 and returns 1 if true and 0 if false
+    Float_t precision = 0.00001;
+    if (((f1 - precision) < f2) &&
+       ((f1 + precision) > f2))
+    {
+       return 1;
+    }
+    else
+    {
+       return 0;
+    }
+}
+
+
+//________________________________________________________________________
+void AliTPCcalibResidualPID::Terminate(const Option_t *)
+{
+
+
+}
+
+
+
+//________________________________________________________________________
+void AliTPCcalibResidualPID::BinLogAxis(const THnSparseF *h, Int_t axisNumber) {
+  //
+  // Method for the correct logarithmic binning of histograms
+  //
+  TAxis *axis = h->GetAxis(axisNumber);
+  int bins = axis->GetNbins();
+
+  Double_t from = axis->GetXmin();
+  Double_t to = axis->GetXmax();
+  Double_t *newBins = new Double_t[bins + 1];
+   
+  newBins[0] = from;
+  Double_t factor = pow(to/from, 1./bins);
+  
+  for (int i = 1; i <= bins; i++) {
+   newBins[i] = factor * newBins[i-1];
+  }
+  axis->Set(bins, newBins);
+  delete [] newBins;
+  
+}
+
+
+//________________________________________________________________________
+Double_t* AliTPCcalibResidualPID::ExtractResidualPID(THnSparseF * histPidQA, const Bool_t useV0s, const Char_t * outFile,
+                                                     const Char_t * type, const Char_t * period, const Char_t * pass,
+                                                     const Char_t * system, const Double_t * initialParameters,
+                                                     const Char_t *dedxtype,
+                                                     AliTPCcalibResidualPID::FitType fitType) {
+  //
+  // (1.) obtain residual graphs
+  //
+  TObjArray * arr = 0x0;
+  
+  Bool_t isMC = kFALSE;
+  if (strcmp(type, "MC") == 0) {
+    arr = GetResidualGraphsMC(histPidQA, system);
+    isMC = kTRUE;
+  }
+  else if (strcmp(type, "DATA") == 0) {
+    arr = GetResidualGraphs(histPidQA, system, useV0s);
+  }
+  else {
+    Printf("ERROR - ExtractResidualPID: Unknown type \"%s\" - must be \"MC\" or \"DATA\"!", type);
+    
+    return 0x0;
+  }
+  
+  Bool_t isPPb = kFALSE;
+  if (strcmp(system, "PPB") == 0 || strcmp(system, "PBP") == 0) {
+    Printf("p-Pb/Pb-p detected - Applying special handling!");
+    isPPb = kTRUE;
+  }
+  //
+  // (2.) get old-style Bethe-Bloch parameters
+  //
+  TF1* parametrisation = FitBB(arr, isMC, isPPb, useV0s, initialParameters, fitType);
+  //
+  // (3.) obtain response functions to OADB
+  //
+  TObjArray* arrResponse = GetResponseFunctions(parametrisation, arr, type, period, pass, system, dedxtype);
+  //
+  // (4.) write results to file and exit
+  //
+  if (arrResponse) {
+    TFile outputFile(outFile,"RECREATE");
+    arrResponse->Write();
+    outputFile.Close();
+  }
+  //
+  
+  return parametrisation->GetParameters();
+}
+
+
+//________________________________________________________________________
+TObjArray * AliTPCcalibResidualPID::GetSeparation(THnSparseF * histPidQA, Int_t kParticle1, Int_t kParticle2) {
+
+  //get THnSparse
+  THnSparse * hist = histPidQA;
+
+
+  Float_t nSigmaPlus1 = 0, nSigmaMinus1 = 0; //sigma of first particle in the TPC
+  Float_t nSigmaPlus2 = 0, nSigmaMinus2 = 0; //sigma of second particle in the TPC
+  
+  if(kParticle1 == 0){ // electron
+       nSigmaMinus1   =  -1.999;
+    nSigmaPlus1   =  2.999;
+  } else if(kParticle1 == 1){ //pion
+       nSigmaMinus1   =  -1.999;
+    nSigmaPlus1   =  2.999;
+  } else if(kParticle1 == 2){ //kaons
+       nSigmaMinus1   =  -2.999;
+    nSigmaPlus1   =    2.999;
+  } else if(kParticle1 == 3){ //protons
+       nSigmaMinus1   =  -2.999;
+    nSigmaPlus1   =    2.999;
+  }
+  
+  //
+  // 1. select and fit first particle
+  //
+  hist->GetAxis(3)->SetRangeUser(kParticle1,kParticle1);
+  hist->GetAxis(4)->SetRangeUser(nSigmaMinus1,nSigmaPlus1); 
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  TH2D * histPart1 = (TH2D*) hist->Projection(1,0)->Clone("histPart1");
+  histPart1->SetTitle(";p /(GeV/c) ; TPCSignal /(a.u.)");
+  histPart1->RebinX(4);
+
+  TObjArray arr;
+  histPart1->FitSlicesY(0,0,-1,10,"QNR",&arr);
+  TH1D * PartMean1 = (TH1D *) arr.At(1)->Clone("PartMean1");
+  TH1D * PartSigma1 = (TH1D *) arr.At(2)->Clone("PartSigma1");
+  PartMean1->SetTitle(";p /(GeV/c) ; Mean (Gauss)");
+  PartSigma1->SetTitle(";p /(GeV/c) ; Sigma (Gauss)");
+
+  histPart1->SetMarkerStyle(22);
+  PartMean1->SetMarkerStyle(21);
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  
+  if(kParticle2==0){ // electron
+       nSigmaMinus2   =  -1.999;
+    nSigmaPlus2   =    2.999;
+  } else if(kParticle2 == 1){ //pion
+       nSigmaMinus2   =  -1.999;
+    nSigmaPlus2   =    2.999;
+  } else if(kParticle2 == 2){ //kaons
+       nSigmaMinus2   =  -2.999;
+    nSigmaPlus2   =    2.999;
+  } else if(kParticle2 == 3){ //protons
+       nSigmaMinus2   =  -2.999;
+    nSigmaPlus2   =    2.999;
+  }
+
+  //
+  // 2. select and fit second particle
+  //
+  hist->GetAxis(3)->SetRangeUser(kParticle2,kParticle2);
+  hist->GetAxis(4)->SetRangeUser(nSigmaMinus2,nSigmaPlus2); 
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  TH2D * histPart2 = (TH2D*)hist->Projection(1,0)->Clone("histPart2");
+  histPart2->RebinX(4);
+  histPart2->FitSlicesY(0,0,-1,10,"QNR",&arr);
+  TH1D * PartMean2 =  (TH1D *) arr.At(1)->Clone("PartMean2");
+  TH1D * PartSigma2 =  (TH1D *) arr.At(2)->Clone("PartSigma2");
+  PartMean2->SetTitle(";p /(GeV/c) ; Mean (Gauss)");
+  PartSigma2->SetTitle(";p /(GeV/c) ; Sigma (Gauss)");
+  PartMean2->SetMarkerStyle(20);
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+
+  //
+  // 3. separation
+  //
+
+       TH1F *fHistSeparation=(TH1F*)PartMean1->Clone("fHistSeparation"); //to get same binning
+        fHistSeparation->SetMarkerStyle(22);
+       const Int_t Nbins = PartMean1->GetNbinsX();
+       fHistSeparation->SetTitle(";p /(GeV/c) ; Separation");
+
+       Float_t DeltaMean[Nbins] ;
+       Float_t DeltaSigma[Nbins];
+
+       for(Int_t i=0 ; i<Nbins; i++){
+       
+       DeltaMean[i] = TMath::Abs(PartMean1->GetBinContent(i) - PartMean2->GetBinContent(i));
+       DeltaSigma[i] = TMath::Abs((PartSigma1->GetBinContent(i) + PartSigma2->GetBinContent(i)))/2.;
+
+       if(!(TMath::Abs(DeltaSigma[i])<0.000001))fHistSeparation->SetBinContent(i,DeltaMean[i]/DeltaSigma[i]);
+       }//for(Int_t i=0 ; i<Nbins ; i++)
+
+       TObjArray *array = new TObjArray();
+       array->Add(histPart1);
+       array->Add(histPart2);
+       array->Add(PartMean1);
+       array->Add(PartSigma1);
+       array->Add(PartMean2);
+       array->Add(PartSigma2);
+       array->Add(fHistSeparation);
+       return array;
+
+}
+
+
+//________________________________________________________________________
+TObjArray * AliTPCcalibResidualPID::GetResidualGraphs(THnSparseF * histPidQA, const Char_t * system, Bool_t useV0s) {
+  //
+  // Extracts the residual graphs from THnSparse created from data (NOT MC!)
+  //
+  
+  const TString momTitle = "#it{p}_{TPC} (GeV/#it{c})";
+  const TString dEdxTitle = "d#it{E}/d#it{x} (arb. unit)";
+  
+  Bool_t isPPb = kFALSE;
+  if (strcmp(system, "PPB") == 0 || strcmp(system, "PBP") == 0) {
+    Printf("p-Pb/Pb-p detected - Applying special handling!");
+    isPPb = kTRUE;
+  }
+  
+  // the following parameters have to be extracted from the data itself
+  //
+    
+  Float_t nSigmaPionMinus   =  -3.999;
+  Float_t nSigmaPionPlus    =   3.999;
+  Float_t nSigmaKaonMinus   =  -4.199;
+  Float_t nSigmaKaonPlus    =  4.7999;
+  Float_t nSigmaElectronMinus = -1.999;
+  Float_t nSigmaElectronPlus  =  4.999;
+  
+  Int_t cutForFitting = 10;
+  Double_t heightFractionForFittingRange = 0.1;
+  //
+  THnSparse * hist = histPidQA;
+  //
+  TCanvas * canvasQAtpc = new TCanvas("canvasQAtpcResGraph","Control canvas for residual graphs (TPC)",100,10,1380,800);
+  canvasQAtpc->Divide(2,2);
+  
+  TCanvas * canvasQAtof = new TCanvas("canvasQAtofResGraph","Control canvas for residual graphs (TOF)",100,10,1380,800);
+  canvasQAtof->Divide(2,2);
+  
+  TCanvas * canvasQAv0 = new TCanvas("canvasQAv0ResGraph","Control canvas for residual graphs (V0)",100,10,1380,800);
+  canvasQAv0->Divide(2,2);
+  
+  TCanvas * canvasQAv0plusTOF = new TCanvas("canvasQAv0plusTOFResGraph","Control canvas for residual graphs (V0+TOF)",100,10,1380,800);
+  canvasQAv0plusTOF->Divide(2,2);
+  
+  
+  TCanvas * canvasQAv0DeDxPurityEl = new TCanvas("canvasQAv0DeDxPurityEl","Control canvas for residual graphs (V0 el)",100,10,600,400);
+  TCanvas * canvasQAv0DeDxPurityPi = new TCanvas("canvasQAv0DeDxPurityPi","Control canvas for residual graphs (V0 pi)",100,10,600,400);
+  TCanvas * canvasQAv0DeDxPurityPr = new TCanvas("canvasQAv0DeDxPurityPr","Control canvas for residual graphs (V0 pr)",100,10,600,400);
+  
+  canvasQAv0DeDxPurityEl->SetLogx();
+  canvasQAv0DeDxPurityPi->SetLogx();
+  canvasQAv0DeDxPurityPr->SetLogx();
+  
+  canvasQAv0DeDxPurityEl->SetLogz();
+  canvasQAv0DeDxPurityPi->SetLogz();
+  canvasQAv0DeDxPurityPr->SetLogz();
+  
+  canvasQAv0DeDxPurityEl->SetTopMargin(0.03);
+  canvasQAv0DeDxPurityPi->SetTopMargin(0.03);
+  canvasQAv0DeDxPurityPr->SetTopMargin(0.03);
+  
+  for (Int_t i = 1; i <= 4; i++) {
+    canvasQAtpc->GetPad(i)->SetGrid(1, 1);
+    canvasQAtof->GetPad(i)->SetGrid(1, 1);
+    canvasQAv0->GetPad(i)->SetGrid(1, 1);
+    canvasQAv0plusTOF->GetPad(i)->SetGrid(1, 1);
+
+    canvasQAtpc->GetPad(i)->SetLogz();
+    canvasQAtof->GetPad(i)->SetLogz();
+    canvasQAv0->GetPad(i)->SetLogz();
+    canvasQAv0plusTOF->GetPad(i)->SetLogz();
+
+    canvasQAtpc->GetPad(i)->SetLogx();
+    canvasQAtof->GetPad(i)->SetLogx();
+    canvasQAv0->GetPad(i)->SetLogx();
+    canvasQAv0plusTOF->GetPad(i)->SetLogx();
+  }
+  //
+  // 1. select and fit electrons
+  //
+  hist->GetAxis(2)->SetRangeUser(0,0); // Non-V0s
+  hist->GetAxis(3)->SetRangeUser(0,0); // electrons
+  hist->GetAxis(4)->SetRangeUser(nSigmaElectronMinus,nSigmaElectronPlus); // 3sigma in TPC 
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  TH2D * histElectron = hist->Projection(1,0);
+  histElectron->SetName("histElectron");
+  histElectron->RebinX(4);
+  histElectron->GetXaxis()->SetRangeUser(0.2,3.0);
+  TObjArray arr;
+  FitSlicesY(histElectron, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * electronPoints = (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * electronGraph = new TGraphErrors(electronPoints);
+  electronGraph->SetName("electronGraph");
+  for(Int_t ip = 0; ip < electronGraph->GetN(); ip ++) {
+    Bool_t removePoint = electronGraph->GetY()[ip] < 10 || 
+      electronGraph->GetEY()[ip]/electronGraph->GetY()[ip] > 0.05 || 
+      electronGraph->GetX()[ip] > 2.0 || 
+      electronGraph->GetX()[ip] < 0.5; 
+    if (removePoint) {
+      electronGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAtof->cd(1);
+  histElectron->SetTitle("electrons");
+  histElectron->GetYaxis()->SetRangeUser(50, 120);
+  histElectron->GetXaxis()->SetTitle(momTitle.Data());
+  histElectron->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histElectron->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histElectron->GetXaxis()->SetNoExponent(kTRUE);
+  histElectron->Draw("colz");
+  electronPoints->SetMarkerStyle(24);
+  electronPoints->Draw("same");
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  //
+  // 2. protons
+  //
+  hist->GetAxis(2)->SetRangeUser(0,0); // Non-V0s
+  hist->GetAxis(3)->SetRangeUser(3,3); // protons
+  //
+  //hist->GetAxis(4)->SetRangeUser(nSigmaProtonMinus,nSigmaProtonPlus); // protons --> not reliable, use PATTERN RECOGNITION
+  TH2D * histProtonTPC = hist->Projection(1,0);
+  histProtonTPC->SetName("histProtonTPC");
+  histProtonTPC->GetXaxis()->SetRangeUser(0.15,0.7);
+  histProtonTPC->GetYaxis()->SetRangeUser(50, hist->GetAxis(1)->GetBinUpEdge(hist->GetAxis(1)->GetNbins()));
+  
+  // PATTERN RECOGNITION
+  //OLD TF1 betaSq("betaSq","50./TMath::Power(x,1.3)",0.1,10); 
+  // Add some additional Erf - cuts away kaons for pPb and PbPb and does not hurt in pp
+  TF1 betaSq("betaSq","50./TMath::Power(x,1.3) + (1-TMath::Erf((x-0.2) / 0.075)) * 100",0.1,10);
+  for(Int_t ix = 1; ix <= histProtonTPC->GetXaxis()->GetNbins(); ix++) {
+     for(Int_t jy = 1; jy <= histProtonTPC->GetYaxis()->GetNbins(); jy++) {
+       Float_t yPos = histProtonTPC->GetYaxis()->GetBinCenter(jy);
+       Float_t xPos = histProtonTPC->GetXaxis()->GetBinCenter(ix);
+       Int_t bin = histProtonTPC->GetBin(ix,jy);
+       if (yPos < betaSq.Eval(xPos)) histProtonTPC->SetBinContent(bin,0);
+     }
+  }
+  //
+  //TODO Use likelihood option -> Inside fitting range ~no contamination, but little statistics at low momenta requires L
+  FitSlicesY(histProtonTPC, heightFractionForFittingRange, cutForFitting, "QNRL", &arr);
+  TH1D * protonPointsTPC = (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * protonTpcGraph = new TGraphErrors(protonPointsTPC);
+  protonTpcGraph->SetName("protonTpcGraph");
+  for(Int_t ip = 0; ip < protonTpcGraph->GetN(); ip ++) {
+    Bool_t removePoint = protonTpcGraph->GetY()[ip] < 10 || protonTpcGraph->GetEY()[ip]/protonTpcGraph->GetY()[ip] > 0.05 // TODO Larger tolerance, since this is only for the correction function, where 5% are still better than no data point
+                        || protonTpcGraph->GetX()[ip] > (useV0s ? (isPPb ? 0.499 : 0.349) : 0.649); // If V0s are to be used, don't use TPC only protons to too high momenta; for pPb low statistics for the moment, therefore, go a bit further with TPC protons
+                        //TODO NOW -> Changed range of TPC-signal -> Hopefully, sufficient statistics now for very low momenta!|| protonTpcGraph->GetX()[ip] < 0.19; // Added this cut because almost always bad statistics below 0.19
+    if (removePoint) {
+      protonTpcGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  TH2D * histProtonTOF = hist->Projection(1,0);
+  histProtonTOF->SetName("histProtonTOF");
+  histProtonTOF->GetXaxis()->SetRangeUser(0.6,2.5);
+  
+  // Same pattern recognition as before
+  for(Int_t ix = 1; ix <= histProtonTOF->GetXaxis()->GetNbins(); ix++) {
+    for(Int_t jy = 1; jy <= histProtonTOF->GetYaxis()->GetNbins(); jy++) {
+      Float_t yPos = histProtonTOF->GetYaxis()->GetBinCenter(jy);
+      Float_t xPos = histProtonTOF->GetXaxis()->GetBinCenter(ix);
+      Int_t bin = histProtonTOF->GetBin(ix,jy);
+      if (yPos < betaSq.Eval(xPos)) histProtonTOF->SetBinContent(bin,0);
+    }
+  }
+  
+  FitSlicesY(histProtonTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+
+  TH1D * protonPointsTOF =  (TH1D *)arr.At(1)->Clone();
+  //
+  TGraphErrors * protonTofGraph = new TGraphErrors(protonPointsTOF);
+  protonTofGraph->SetName("protonTofGraph");
+  for(Int_t ip = 0; ip < protonTofGraph->GetN(); ip ++) {
+    // If V0s are to be used, TPC+TOF protons are only for reference and can be plotted to higher momenta
+    Bool_t removePoint = protonTofGraph->GetY()[ip] < 10 || protonTofGraph->GetEY()[ip]/protonTofGraph->GetY()[ip] > 0.02 || protonTofGraph->GetX()[ip] > (useV0s ? 3 : 2) || protonTofGraph->GetX()[ip] < 0.65;
+    if (removePoint) {
+      protonTofGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAtpc->cd(2);
+  histProtonTPC->SetTitle("protons");
+  histProtonTPC->GetXaxis()->SetTitle(momTitle.Data());
+  histProtonTPC->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histProtonTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histProtonTPC->GetXaxis()->SetNoExponent(kTRUE);
+  histProtonTPC->Draw("colz");
+  betaSq.DrawCopy("same");
+  protonPointsTPC->SetMarkerStyle(20);
+  protonPointsTOF->SetMarkerStyle(24);
+  protonPointsTOF->Draw("same");
+  protonPointsTPC->Draw("same");
+  //
+  protonTpcGraph->SetMarkerStyle(26);
+  protonTpcGraph->SetMarkerColor(kMagenta);
+  protonTpcGraph->DrawClone("p");
+  protonTofGraph->SetMarkerStyle(25);
+  protonTofGraph->SetMarkerColor(kMagenta);
+  protonTofGraph->DrawClone("p");
+  
+  canvasQAtof->cd(2);
+  histProtonTOF->SetTitle("protons");
+  histProtonTOF->GetYaxis()->SetRangeUser(30, 250);
+  histProtonTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histProtonTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histProtonTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histProtonTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histProtonTOF->Draw("colz");
+  betaSq.DrawCopy("same");
+  protonPointsTOF->Draw("same");
+  protonPointsTPC->Draw("same");
+  //
+  protonTpcGraph->DrawClone("p");
+  protonTofGraph->DrawClone("p");
+  
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  //
+  // 3. pions
+  //
+  hist->GetAxis(2)->SetRangeUser(0,0); // Non-V0s
+  hist->GetAxis(3)->SetRangeUser(1,1); // pions
+  //
+  Double_t lowerPionThreshold = 0.3;
+
+  hist->GetAxis(4)->SetRangeUser(nSigmaPionMinus,nSigmaPionPlus); // pions
+  TH2D * histPionTPC = hist->Projection(1,0);
+  histPionTPC->SetName("histPionTPC");
+  histPionTPC->GetXaxis()->SetRangeUser(0.15,0.7);
+  FitSlicesY(histPionTPC, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+
+  // In case of really bad splines comment last 5 lines and use the following 6 lines:
+  //TH2D * histPionTPC = hist->Projection(1,0);
+  //histPionTPC->SetName("histPionTPC");
+  //histPionTPC->GetYaxis()->SetRangeUser(30,100);
+  //histPionTPC->GetXaxis()->SetRangeUser(0.15,0.7);
+  //FitSlicesY(histPionTPC, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  //lowerPionThreshold = 0.15;
+
+
+  TH1D * pionPointsTPC =  (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * pionTpcGraph = new TGraphErrors(pionPointsTPC);
+  pionTpcGraph->SetName("pionTpcGraph");
+  for(Int_t ip = 0; ip < pionTpcGraph->GetN(); ip ++) {
+    Bool_t removePoint = pionTpcGraph->GetY()[ip] < 10 || pionTpcGraph->GetEY()[ip]/pionTpcGraph->GetY()[ip] > 0.02 || pionTpcGraph->GetX()[ip] > 0.5
+                         || pionTpcGraph->GetX()[ip] < lowerPionThreshold; // Exclude contamination by electrons
+    if (removePoint) {
+      pionTpcGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  TH2D * histPionTOF = hist->Projection(1,0);
+  histPionTOF->SetName("histPionTOF");
+  histPionTOF->GetXaxis()->SetRangeUser(0.5,1.1);
+  FitSlicesY(histPionTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * pionPointsTOF = (TH1D *) arr.At(1)->Clone();
+  TGraphErrors * pionTofGraph = new TGraphErrors(pionPointsTOF);
+  pionTofGraph->SetName("pionTofGraph");
+  for(Int_t ip = 0; ip < pionTofGraph->GetN(); ip ++) {
+    Bool_t removePoint = pionTofGraph->GetY()[ip] < 10 || pionTofGraph->GetEY()[ip]/pionTofGraph->GetY()[ip] > 0.02 || pionTofGraph->GetX()[ip] > 1.1 || pionTofGraph->GetX()[ip] < 0.5; // Changed by Ben (was 0.35) to avoid problems with TOF efficiency/systematics
+    if (removePoint) {
+      pionTofGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  canvasQAtpc->cd(3);
+  histPionTPC->GetYaxis()->SetRangeUser(30, 90);
+  histPionTPC->SetTitle("pions");
+  histPionTPC->GetXaxis()->SetTitle(momTitle.Data());
+  histPionTPC->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histPionTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histPionTPC->GetXaxis()->SetNoExponent(kTRUE);
+  histPionTPC->Draw("colz");
+  pionPointsTPC->SetMarkerStyle(20);
+  pionPointsTOF->SetMarkerStyle(24);
+  pionPointsTOF->Draw("same");
+  pionPointsTPC->Draw("same");
+  //
+  pionTpcGraph->SetMarkerStyle(26);
+  pionTpcGraph->SetMarkerColor(kMagenta);
+  pionTpcGraph->DrawClone("p");
+  pionTofGraph->SetMarkerStyle(25);
+  pionTofGraph->SetMarkerColor(kMagenta);
+  pionTofGraph->DrawClone("p");
+  
+  canvasQAtof->cd(3);
+  histPionTOF->GetYaxis()->SetRangeUser(30, 80);
+  histPionTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histPionTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histPionTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histPionTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histPionTOF->Draw("colz");
+  histPionTOF->SetTitle("pions");
+  pionPointsTOF->Draw("same");
+  pionPointsTPC->Draw("same");
+  //
+  pionTpcGraph->DrawClone("p");
+  pionTofGraph->DrawClone("p");
+
+  
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  //
+  // 4. kaons
+  //
+  hist->GetAxis(2)->SetRangeUser(0,0); // Non-V0s
+  hist->GetAxis(3)->SetRangeUser(2,2); // kaons
+  //
+  hist->GetAxis(4)->SetRangeUser(nSigmaKaonMinus,nSigmaKaonPlus); // kaons
+  TH2D * histKaonTPC = hist->Projection(1,0);
+  histKaonTPC->SetName("histKaonTPC");
+  histKaonTPC->GetXaxis()->SetRangeUser(0.12,0.6999);
+  FitSlicesY(histKaonTPC, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * kaonPointsTPC = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * kaonTpcGraph = new TGraphErrors(kaonPointsTPC);
+  kaonTpcGraph->SetName("kaonTpcGraph");
+  for(Int_t ip = 0; ip < kaonTpcGraph->GetN(); ip ++) {
+    Bool_t removePoint = kaonTpcGraph->GetY()[ip] < 10 || kaonTpcGraph->GetEY()[ip]/kaonTpcGraph->GetY()[ip] > 0.02
+                         || kaonTpcGraph->GetX()[ip] < 0.12 || kaonTpcGraph->GetX()[ip] > 0.319; //TODO BEN was > 0.399
+    if (removePoint) {
+      kaonTpcGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // sigma in TOF
+  
+  /*
+  hist->GetAxis(5)->SetRangeUser(-1.999,1.999); // sigma in TOF
+  
+  if (hist->GetAxis(3)->GetNbins() >= 5) {
+    printf("Using restricted eta range for TPC+TOF Kaons!\n");
+    hist->GetAxis(3)->SetRangeUser(4,4);
+  }
+  else {
+    printf("WARNING: Data points for restricted eta range for TPC+TOF Kaons not available! Using full eta range (worse w.r.t. contamination)\n");
+  }*/
+  
+  TH2D * histKaonTOF = hist->Projection(1,0);
+  histKaonTOF->SetName("histKaonTOF");
+  histKaonTOF->GetXaxis()->SetRangeUser(0.3,1.5);
+  FitSlicesY(histKaonTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * kaonPointsTOF = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * kaonTofGraph = new TGraphErrors(kaonPointsTOF);
+  kaonTofGraph->SetName("kaonTofGraph");
+  //
+  for(Int_t ip = 0; ip < kaonTofGraph->GetN(); ip ++) {
+    Bool_t removePoint = kaonTofGraph->GetY()[ip] < 10 || kaonTofGraph->GetEY()[ip]/kaonTofGraph->GetY()[ip] > 0.02 || kaonTofGraph->GetX()[ip] > 1.0
+                         || kaonTofGraph->GetX()[ip] < 0.36; //TODO BEN NOW was 0.5
+    if (removePoint) {
+      kaonTofGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAtpc->cd(4);
+  histKaonTPC->SetTitle("kaons");
+  histKaonTPC->GetYaxis()->SetRangeUser(30, 500);
+  histKaonTPC->GetXaxis()->SetTitle(momTitle.Data());
+  histKaonTPC->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histKaonTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histKaonTPC->GetXaxis()->SetNoExponent(kTRUE);
+  histKaonTPC->Draw("colz");
+  kaonPointsTPC->SetMarkerStyle(20);
+  kaonPointsTOF->SetMarkerStyle(24);
+  kaonPointsTOF->Draw("same");
+  kaonPointsTPC->Draw("same");
+  //
+  kaonTpcGraph->SetMarkerStyle(26);
+  kaonTpcGraph->SetMarkerColor(kMagenta);
+  kaonTpcGraph->DrawClone("p");
+  kaonTofGraph->SetMarkerStyle(25);
+  kaonTofGraph->SetMarkerColor(kMagenta);
+  kaonTofGraph->DrawClone("p");
+
+  
+  canvasQAtof->cd(4);
+  histKaonTOF->GetYaxis()->SetRangeUser(30, 250);
+  histKaonTOF->SetTitle("kaons");
+  histKaonTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histKaonTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histKaonTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histKaonTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histKaonTOF->Draw("colz");
+  kaonPointsTOF->Draw("same");
+  kaonPointsTPC->Draw("same");
+  kaonTpcGraph->DrawClone("p");
+  kaonTofGraph->DrawClone("p");
+  
+  hist->GetAxis(4)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  
+  
+  //
+  // 5. V0 electrons
+  //
+  hist->GetAxis(2)->SetRangeUser(1,1); // V0 electrons
+  hist->GetAxis(3)->SetRangeUser(0,0); // electrons
+  
+  //
+  TH2D * histElectronV0 = hist->Projection(1,0);
+  histElectronV0->SetName("histElectronV0");
+  //histElectronV0->RebinX(2);
+  histElectronV0->GetXaxis()->SetRangeUser(0.15,5.0);
+  FitSlicesY(histElectronV0, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * electronPointsV0 = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * electronV0Graph = new TGraphErrors(electronPointsV0);
+  electronV0Graph->SetName("electronV0Graph");
+  for(Int_t ip = 0; ip < electronV0Graph->GetN(); ip ++) {
+    Bool_t removePoint = electronV0Graph->GetY()[ip] < 10 || electronV0Graph->GetEY()[ip]/electronV0Graph->GetY()[ip] > 0.02;
+
+    if (removePoint) {
+      electronV0Graph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0->cd(1);
+  histElectronV0->SetTitle("V0 electrons");
+  histElectronV0->GetYaxis()->SetRangeUser(50, 120);
+  histElectronV0->GetXaxis()->SetTitle(momTitle.Data());
+  histElectronV0->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histElectronV0->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histElectronV0->GetXaxis()->SetNoExponent(kTRUE);
+  histElectronV0->SetStats(0);
+  histElectronV0->Draw("colz");
+  electronPointsV0->SetMarkerStyle(34);
+  electronPointsV0->Draw("same");
+  electronGraph->SetMarkerStyle(25);
+  electronGraph->SetMarkerColor(kMagenta);
+  electronGraph->DrawClone("p");
+  
+  canvasQAv0DeDxPurityEl->cd();
+  gStyle->SetOptTitle(0);
+  TH1D* hElNew = (TH1D*)histElectronV0->DrawClone("colz");
+  hElNew->GetXaxis()->SetTitleOffset(1.0);
+  hElNew->SetTitle("");
+  electronPointsV0->DrawClone("same");
+  gStyle->SetOptTitle(1);
+
+
+
+  canvasQAtof->cd(1);
+  electronV0Graph->SetMarkerStyle(24);
+  electronV0Graph->SetMarkerColor(kMagenta);
+  electronV0Graph->DrawClone("p");
+  
+  //
+  // 6. V0 pions
+  //
+  hist->GetAxis(2)->SetRangeUser(2,2); // V0 pions
+  hist->GetAxis(3)->SetRangeUser(1,1); // pions
+  //
+  TH2D * histPionV0 = hist->Projection(1,0);
+  histPionV0->SetName("histPionV0");
+  histPionV0->GetXaxis()->SetRangeUser(0.15,10.);
+  FitSlicesY(histPionV0, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * pionPointsV0 = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * pionV0Graph = new TGraphErrors(pionPointsV0);
+  pionV0Graph->SetName("pionV0Graph");
+  for(Int_t ip = 0; ip < pionV0Graph->GetN(); ip ++) {
+    Bool_t removePoint = pionV0Graph->GetY()[ip] < 10 || pionV0Graph->GetEY()[ip]/pionV0Graph->GetY()[ip] > 0.02;
+
+    if (removePoint) {
+      pionV0Graph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0->cd(3);
+  histPionV0->SetTitle("V0 pions");
+  histPionV0->GetYaxis()->SetRangeUser(30, 100);
+  histPionV0->GetXaxis()->SetTitle(momTitle.Data());
+  histPionV0->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histPionV0->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histPionV0->GetXaxis()->SetNoExponent(kTRUE);
+  histPionV0->Draw("colz");
+  pionPointsV0->SetMarkerStyle(34);
+  pionPointsV0->Draw("same");
+  pionTofGraph->DrawClone("p");
+  pionTpcGraph->DrawClone("p");
+  
+  canvasQAv0DeDxPurityPi->cd();
+  gStyle->SetOptTitle(0);
+  TH1D* hPiNew = (TH1D*)histPionV0->DrawClone("colz");
+  hPiNew->GetXaxis()->SetTitleOffset(1.0);
+  hPiNew->SetTitle("");
+  pionPointsV0->DrawClone("same");
+  gStyle->SetOptTitle(1);
+  
+  canvasQAtof->cd(3);
+  pionV0Graph->SetMarkerStyle(24);
+  pionV0Graph->SetMarkerColor(kMagenta);
+  pionV0Graph->DrawClone("p");
+
+  canvasQAtpc->cd(3);
+  pionV0Graph->DrawClone("p");
+  
+  //
+  // 6. V0 protons
+  //
+  hist->GetAxis(2)->SetRangeUser(3,3); // V0 protons
+  hist->GetAxis(3)->SetRangeUser(3,3); // protons
+  //
+  TH2D * histProtonV0 = hist->Projection(1,0);
+  histProtonV0->SetName("histProtonV0");
+  histProtonV0->GetXaxis()->SetRangeUser(0.15,10.);
+  
+  // Remove contamination due to el and pions and low momenta because there the statistics
+  // for the V0 protons goes down and becomes comparable with the contamination
+  // -> This spoils the fit, if the contamination is not removed
+  
+  // PATTERN RECOGNITION
+  Int_t correctionUpToBin = histProtonV0->GetXaxis()->FindBin(0.6);
+  
+  for(Int_t ix = 1; ix <= correctionUpToBin; ix++) {
+     for(Int_t jy = 1; jy <= histProtonV0->GetYaxis()->GetNbins(); jy++) {
+       Float_t yPos = histProtonV0->GetYaxis()->GetBinCenter(jy);
+       Float_t xPos = histProtonV0->GetXaxis()->GetBinCenter(ix);
+       Int_t bin = histProtonV0->GetBin(ix,jy);
+       if (yPos < betaSq.Eval(xPos)) histProtonV0->SetBinContent(bin,0);
+     }
+  }
+  
+  /*
+  Int_t correctionUpToBin = histProtonV0->GetXaxis()->FindBin(0.4);
+  Double_t threshold = 120.;
+  
+  for(Int_t ix = 1; ix <= correctionUpToBin; ix++) {
+     for(Int_t jy = 1; jy <= histProtonV0->GetYaxis()->GetNbins(); jy++) {
+       Float_t yPos = histProtonV0->GetYaxis()->GetBinCenter(jy);
+       Int_t bin = histProtonV0->GetBin(ix,jy);
+       if (yPos < threshold) histProtonV0->SetBinContent(bin,0);
+       else break;
+     }
+  }
+  */
+  
+  FitSlicesY(histProtonV0, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * protonPointsV0 = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * protonV0Graph = new TGraphErrors(protonPointsV0);
+  protonV0Graph->SetName("protonV0Graph");
+  for(Int_t ip = 0; ip < protonV0Graph->GetN(); ip ++) {
+    Bool_t removePoint = protonV0Graph->GetY()[ip] < 10 || protonV0Graph->GetEY()[ip]/protonV0Graph->GetY()[ip] > 0.02;
+                         
+    if (removePoint) {
+      protonV0Graph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0->cd(2);
+  histProtonV0->SetTitle("V0 protons");
+  histProtonV0->GetXaxis()->SetTitle(momTitle.Data());
+  histProtonV0->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histProtonV0->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histProtonV0->GetXaxis()->SetNoExponent(kTRUE);
+  histProtonV0->Draw("colz");
+  protonPointsV0->SetMarkerStyle(34);
+  protonPointsV0->Draw("same");
+  protonTofGraph->DrawClone("p");
+  protonTpcGraph->DrawClone("p");
+  
+  canvasQAv0DeDxPurityPr->cd();
+  gStyle->SetOptTitle(0);
+  TH1D* hPrNew = (TH1D*)histProtonV0->DrawClone("colz");
+  hPrNew->GetXaxis()->SetTitleOffset(1.0);
+  hPrNew->SetTitle("");
+  protonPointsV0->DrawClone("same");
+  gStyle->SetOptTitle(1);
+
+  canvasQAtof->cd(2);
+  protonV0Graph->SetMarkerStyle(24);
+  protonV0Graph->SetMarkerColor(kMagenta);
+  protonV0Graph->DrawClone("p");
+
+  canvasQAtpc->cd(2);
+  protonV0Graph->DrawClone("p");
+  
+  
+  hist->GetAxis(2)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(3)->SetRange(0,-1); // RESET RANGES
+  
+  
+  //
+  // 5. V0 electrons + TOF
+  //
+  hist->GetAxis(2)->SetRangeUser(1,1); // V0 electrons
+  hist->GetAxis(3)->SetRangeUser(0,0); // electrons
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  //
+  TH2D * histElectronV0plusTOF = hist->Projection(1,0);
+  histElectronV0plusTOF->SetName("histElectronV0plusTOF");
+  histElectronV0plusTOF->RebinX(2);
+  histElectronV0plusTOF->GetXaxis()->SetRangeUser(0.2,5.0);
+  FitSlicesY(histElectronV0plusTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * electronPointsV0plusTOF = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * electronV0plusTOFGraph = new TGraphErrors(electronPointsV0plusTOF);
+  electronV0plusTOFGraph->SetName("electronV0plusTOFGraph");
+  for(Int_t ip = 0; ip < electronV0plusTOFGraph->GetN(); ip ++) {
+    Bool_t removePoint = electronV0plusTOFGraph->GetY()[ip] < 10 || electronV0plusTOFGraph->GetEY()[ip]/electronV0plusTOFGraph->GetY()[ip] > 0.02;
+
+    if (removePoint) {
+      electronV0plusTOFGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0plusTOF->cd(1);
+  histElectronV0plusTOF->SetTitle("V0+TOF electrons");
+  histElectronV0plusTOF->GetYaxis()->SetRangeUser(50, 120);
+  histElectronV0plusTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histElectronV0plusTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histElectronV0plusTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histElectronV0plusTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histElectronV0plusTOF->Draw("colz");
+  electronPointsV0plusTOF->SetMarkerStyle(29);
+  electronPointsV0plusTOF->Draw("same");
+  electronGraph->DrawClone("p");
+  electronV0Graph->DrawClone("p");
+
+  canvasQAv0->cd(1);
+  electronV0plusTOFGraph->SetMarkerStyle(30);
+  electronV0plusTOFGraph->SetMarkerColor(kMagenta);
+  electronV0plusTOFGraph->DrawClone("p");
+
+  canvasQAtof->cd(1);
+  electronV0plusTOFGraph->DrawClone("p");
+  
+  //
+  // 6. V0 pions + TOF
+  //
+  hist->GetAxis(2)->SetRangeUser(2,2); // V0 pions
+  hist->GetAxis(3)->SetRangeUser(1,1); // pions
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  //
+  TH2D * histPionV0plusTOF = hist->Projection(1,0);
+  histPionV0plusTOF->SetName("histPionV0plusTOF");
+  histPionV0plusTOF->GetXaxis()->SetRangeUser(0.15,10.);
+  FitSlicesY(histPionV0plusTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * pionPointsV0plusTOF = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * pionV0plusTOFGraph = new TGraphErrors(pionPointsV0plusTOF);
+  pionV0plusTOFGraph->SetName("pionV0plusTOFGraph");
+  for(Int_t ip = 0; ip < pionV0plusTOFGraph->GetN(); ip ++) {
+    Bool_t removePoint = pionV0plusTOFGraph->GetY()[ip] < 10 || pionV0plusTOFGraph->GetEY()[ip]/pionV0plusTOFGraph->GetY()[ip] > 0.02;
+
+    if (removePoint) {
+      pionV0plusTOFGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0plusTOF->cd(3);
+  histPionV0plusTOF->SetTitle("V0+TOF pions");
+  histPionV0plusTOF->GetYaxis()->SetRangeUser(30, 100);
+  histPionV0plusTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histPionV0plusTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histPionV0plusTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histPionV0plusTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histPionV0plusTOF->Draw("colz");
+  pionPointsV0plusTOF->SetMarkerStyle(29);
+  pionPointsV0plusTOF->Draw("same");
+  pionV0Graph->DrawClone("p");
+  pionTofGraph->DrawClone("p");
+  pionTpcGraph->DrawClone("p");
+  
+  canvasQAv0->cd(3);
+  pionV0plusTOFGraph->SetMarkerStyle(30);
+  pionV0plusTOFGraph->SetMarkerColor(kMagenta);
+  pionV0plusTOFGraph->DrawClone("p");
+  
+  canvasQAtof->cd(3);
+  pionV0plusTOFGraph->DrawClone("p");
+  
+  canvasQAtpc->cd(3);
+  pionV0plusTOFGraph->DrawClone("p");
+  
+  //
+  // 6. V0 protons + TOF
+  //
+  hist->GetAxis(2)->SetRangeUser(3,3); // V0 protons
+  hist->GetAxis(3)->SetRangeUser(3,3); // protons
+  hist->GetAxis(5)->SetRangeUser(-2.999,2.999); // 3sigma in TOF
+  //
+  TH2D * histProtonV0plusTOF = hist->Projection(1,0);
+  histProtonV0plusTOF->SetName("histProtonV0plusTOF");
+  histProtonV0plusTOF->GetXaxis()->SetRangeUser(0.15,10.);
+  
+  // Remove contamination due to el and pions and low momenta because there the statistics
+  // for the V0 protons goes down and becomes comparable with the contamination
+  // -> This spoils the fit, if the contamination is not removed
+  
+  // PATTERN RECOGNITION
+  correctionUpToBin = histProtonV0plusTOF->GetXaxis()->FindBin(0.6);
+  
+  for(Int_t ix = 1; ix <= correctionUpToBin; ix++) {
+     for(Int_t jy = 1; jy <= histProtonV0plusTOF->GetYaxis()->GetNbins(); jy++) {
+       Float_t yPos = histProtonV0plusTOF->GetYaxis()->GetBinCenter(jy);
+       Float_t xPos = histProtonV0plusTOF->GetXaxis()->GetBinCenter(ix);
+       Int_t bin = histProtonV0plusTOF->GetBin(ix,jy);
+       if (yPos < betaSq.Eval(xPos)) histProtonV0plusTOF->SetBinContent(bin,0);
+     }
+  }
+  
+  FitSlicesY(histProtonV0plusTOF, heightFractionForFittingRange, cutForFitting, "QNR", &arr);
+  TH1D * protonPointsV0plusTOF = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * protonV0plusTOFGraph = new TGraphErrors(protonPointsV0plusTOF);
+  protonV0plusTOFGraph->SetName("protonV0plusTOFGraph");
+  for(Int_t ip = 0; ip < protonV0plusTOFGraph->GetN(); ip ++) {
+    Bool_t removePoint = protonV0plusTOFGraph->GetY()[ip] < 10 || protonV0plusTOFGraph->GetEY()[ip]/protonV0plusTOFGraph->GetY()[ip] > 0.02;
+                         
+    if (removePoint) {
+      protonV0plusTOFGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAv0plusTOF->cd(2);
+  histProtonV0plusTOF->SetTitle("V0+TOF protons");
+  histProtonV0plusTOF->GetXaxis()->SetTitle(momTitle.Data());
+  histProtonV0plusTOF->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histProtonV0plusTOF->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histProtonV0plusTOF->GetXaxis()->SetNoExponent(kTRUE);
+  histProtonV0plusTOF->Draw("colz");
+  protonPointsV0plusTOF->SetMarkerStyle(29);
+  protonPointsV0plusTOF->Draw("same");
+  protonV0Graph->DrawClone("p");
+  protonTofGraph->DrawClone("p");
+  protonTpcGraph->DrawClone("p");
+
+  canvasQAv0->cd(2);
+  protonV0plusTOFGraph->SetMarkerStyle(30);
+  protonV0plusTOFGraph->SetMarkerColor(kMagenta);
+  protonV0plusTOFGraph->DrawClone("p");
+  
+  canvasQAtof->cd(2);
+  protonV0plusTOFGraph->DrawClone("p");
+
+  canvasQAtpc->cd(2);
+  protonV0plusTOFGraph->DrawClone("p");
+  
+  hist->GetAxis(2)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(3)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(5)->SetRange(0,-1); // RESET RANGES
+  
+  
+  // Clone (V0, V0+TOF) graphs: Remove some points, which are expected to deviate from the Bethe-Bloch curve, from the original graphs, so that
+  // these graphs can be used for the BB fit. The original graph will keep all point in order to determine the correction and response
+  // functions
+  TGraphErrors * electronV0GraphForBBfit = new TGraphErrors(*electronV0Graph);
+  electronV0GraphForBBfit->SetName("electronV0GraphForBBfit");
+  
+  for(Int_t ip = 0; ip < electronV0GraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = electronV0GraphForBBfit->GetX()[ip] < (isPPb ? 1.2 : 0.4);// || electronV0GraphForBBfit->GetX()[ip] > 2.2;
+    
+    if (removePoint) {
+      electronV0GraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  TGraphErrors * pionV0GraphForBBfit = new TGraphErrors(*pionV0Graph);
+  pionV0GraphForBBfit->SetName("pionV0GraphForBBfit");
+  
+  for(Int_t ip = 0; ip < pionV0GraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = pionV0GraphForBBfit->GetX()[ip] < (isPPb ? 1.0 : 0.76);// || pionV0GraphForBBfit->GetX()[ip] > 6.; //TODO NOW was < 0.4 before
+    //Bool_t removePoint = pionV0GraphForBBfit->GetX()[ip] < 0.4 || pionV0GraphForBBfit->GetX()[ip] > 0.399;// In this case NOT used for BB fit
+    if (removePoint) {
+      pionV0GraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  TGraphErrors * protonV0GraphForBBfit = new TGraphErrors(*protonV0Graph);
+  protonV0GraphForBBfit->SetName("protonV0GraphForBBfit");
+  
+  for(Int_t ip = 0; ip < protonV0GraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = protonV0GraphForBBfit->GetX()[ip] < 0.9 || protonV0GraphForBBfit->GetX()[ip] > 5.0; //TODO NOW was 2.5 before
+    //Bool_t removePoint = protonV0GraphForBBfit->GetX()[ip] < 0.9 || protonV0GraphForBBfit->GetX()[ip] > 0.599;// In this case NOT used for BB fit
+    if (removePoint) {
+      protonV0GraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  
+  // V0 plus TOF
+  TGraphErrors * electronV0plusTOFGraphForBBfit = new TGraphErrors(*electronV0plusTOFGraph);
+  electronV0plusTOFGraphForBBfit->SetName("electronV0plusTOFGraphForBBfit");
+  
+  for(Int_t ip = 0; ip < electronV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = electronV0plusTOFGraphForBBfit->GetX()[ip] < (isPPb ? 1.2 : 0.6);//0.4 || electronV0plusTOFGraphForBBfit->GetX()[ip] > 1.3799;
+    if (removePoint) {
+      electronV0plusTOFGraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  TGraphErrors * pionV0plusTOFGraphForBBfit = new TGraphErrors(*pionV0plusTOFGraph);
+  pionV0plusTOFGraphForBBfit->SetName("pionV0plusTOFGraphForBBfit");
+  
+  for(Int_t ip = 0; ip < pionV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = pionV0plusTOFGraphForBBfit->GetX()[ip] < 0.4;// || pionV0plusTOFGraphForBBfit->GetX()[ip] > 6.;
+    if (removePoint) {
+      pionV0plusTOFGraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  TGraphErrors * protonV0plusTOFGraphForBBfit = new TGraphErrors(*protonV0plusTOFGraph);
+  protonV0plusTOFGraphForBBfit->SetName("protonV0plusTOFGraphForBBfit");
+  
+  for(Int_t ip = 0; ip < protonV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    Bool_t removePoint = protonV0plusTOFGraphForBBfit->GetX()[ip] < 0.9 || protonV0plusTOFGraphForBBfit->GetX()[ip] > 2.5;
+    if (removePoint) {
+      protonV0plusTOFGraphForBBfit->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  
+  //
+  // rescale graphs to betaGamma
+  //
+  kaonTofGraph->SetMarkerStyle(21);
+  kaonTpcGraph->SetMarkerStyle(22);
+  for(Int_t ip = 0; ip < kaonTofGraph->GetN(); ip ++) {
+    kaonTofGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+    kaonTofGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+  }
+  for(Int_t ip = 0; ip < kaonTpcGraph->GetN(); ip ++) {
+    kaonTpcGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+    kaonTpcGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+  }
+  //
+  electronGraph->SetMarkerStyle(22);
+  electronV0Graph->SetMarkerStyle(34);
+  electronV0GraphForBBfit->SetMarkerStyle(34);
+  electronV0plusTOFGraph->SetMarkerStyle(30);
+  electronV0plusTOFGraphForBBfit->SetMarkerStyle(30);
+  electronGraph->SetMarkerColor(2);
+  electronV0Graph->SetMarkerColor(2);
+  electronV0GraphForBBfit->SetMarkerColor(2);
+  electronV0plusTOFGraph->SetMarkerColor(2);
+  electronV0plusTOFGraphForBBfit->SetMarkerColor(2);
+  for(Int_t ip = 0; ip < electronGraph->GetN(); ip ++) {
+    electronGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  for(Int_t ip = 0; ip < electronV0Graph->GetN(); ip ++) {
+    electronV0Graph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronV0Graph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  for(Int_t ip = 0; ip < electronV0GraphForBBfit->GetN(); ip ++) {
+    electronV0GraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronV0GraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  for(Int_t ip = 0; ip < electronV0plusTOFGraph->GetN(); ip ++) {
+    electronV0plusTOFGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronV0plusTOFGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  for(Int_t ip = 0; ip < electronV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    electronV0plusTOFGraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronV0plusTOFGraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  //
+  pionTofGraph->SetMarkerStyle(21);
+  pionTpcGraph->SetMarkerStyle(22);
+  pionV0Graph->SetMarkerStyle(34);
+  pionV0GraphForBBfit->SetMarkerStyle(34);
+  pionV0plusTOFGraph->SetMarkerStyle(30);
+  pionV0plusTOFGraphForBBfit->SetMarkerStyle(30);
+  pionTofGraph->SetMarkerColor(3);
+  pionTpcGraph->SetMarkerColor(3);
+  pionV0Graph->SetMarkerColor(3);
+  pionV0GraphForBBfit->SetMarkerColor(3);
+  pionV0plusTOFGraph->SetMarkerColor(3);
+  pionV0plusTOFGraphForBBfit->SetMarkerColor(3);
+  for(Int_t ip = 0; ip < pionTofGraph->GetN(); ip ++) {
+    pionTofGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionTofGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < pionTpcGraph->GetN(); ip ++) {
+    pionTpcGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionTpcGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < pionV0Graph->GetN(); ip ++) {
+    pionV0Graph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionV0Graph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < pionV0GraphForBBfit->GetN(); ip ++) {
+    pionV0GraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionV0GraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < pionV0plusTOFGraph->GetN(); ip ++) {
+    pionV0plusTOFGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionV0plusTOFGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < pionV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    pionV0plusTOFGraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionV0plusTOFGraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  //
+  protonTofGraph->SetMarkerStyle(21);
+  protonTpcGraph->SetMarkerStyle(22);
+  protonV0Graph->SetMarkerStyle(34);
+  protonV0GraphForBBfit->SetMarkerStyle(34);
+  protonV0plusTOFGraph->SetMarkerStyle(30);
+  protonV0plusTOFGraphForBBfit->SetMarkerStyle(30);
+  protonTofGraph->SetMarkerColor(4);
+  protonTpcGraph->SetMarkerColor(4);
+  protonV0Graph->SetMarkerColor(4);
+  protonV0GraphForBBfit->SetMarkerColor(4);
+  protonV0plusTOFGraph->SetMarkerColor(4);
+  protonV0plusTOFGraphForBBfit->SetMarkerColor(4);
+  for(Int_t ip = 0; ip < protonTofGraph->GetN(); ip ++) {
+    protonTofGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonTofGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < protonTpcGraph->GetN(); ip ++) {
+    protonTpcGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonTpcGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < protonV0Graph->GetN(); ip ++) {
+    protonV0Graph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonV0Graph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < protonV0GraphForBBfit->GetN(); ip ++) {
+    protonV0GraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonV0GraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < protonV0plusTOFGraph->GetN(); ip ++) {
+    protonV0plusTOFGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonV0plusTOFGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < protonV0plusTOFGraphForBBfit->GetN(); ip ++) {
+    protonV0plusTOFGraphForBBfit->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonV0plusTOFGraphForBBfit->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  //
+  //
+  TGraphErrors * graphAll = new TGraphErrors();
+  TList * listColl = new TList();
+  if (!useV0s) {
+    //listColl->Add(protonTpcGraph);
+    //listColl->Add(kaonTpcGraph);  
+    listColl->Add(pionTpcGraph);
+    listColl->Add(electronGraph);
+    listColl->Add(protonTofGraph); 
+    listColl->Add(kaonTofGraph);
+    listColl->Add(pionTofGraph);
+  }
+  else {
+    listColl->Add(pionV0GraphForBBfit);
+    //listColl->Add(electronV0GraphForBBfit);
+    listColl->Add(protonV0GraphForBBfit);
+    
+    //listColl->Add(pionV0plusTOFGraphForBBfit);
+    listColl->Add(electronV0plusTOFGraphForBBfit);
+    //listColl->Add(protonV0plusTOFGraphForBBfit);
+  }
+  MergeGraphErrors(graphAll, listColl);
+  graphAll->SetNameTitle("beamDataPoints","beamDataPoints");
+  
+  // Create graph out of TPC only, V0, V0+TOF and TPC+TOF to determine correction functions
+  TList * listCollPr = new TList();
+  if (useV0s) {
+    listCollPr->Add(protonTpcGraph);
+    //listCollPr->Add(protonTofGraph);
+    
+    TGraphErrors * protonV0GraphCut = new TGraphErrors(*protonV0Graph);
+    protonV0GraphCut->SetName("protonV0GraphCut");
+    for(Int_t ip = 0; ip < protonV0GraphCut->GetN(); ip ++) {
+      Double_t mom = protonV0GraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kProton);
+      //Bool_t removePoint = mom >= 0.6 || mom < 0.35; // Will take TPC protons instead (statistics) for very low momenta
+      Bool_t removePoint = mom < (isPPb ? 0.6 : 0.35); // Will take TPC protons instead (statistics) for very low momenta;
+                                                       // for pPb low statistics for the moment, therefore, go a bit further with TPC protons
+      
+      
+      if (removePoint) {
+        protonV0GraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }
+    /*
+    TGraphErrors * protonV0plusTOFGraphCut = new TGraphErrors(*protonV0plusTOFGraph);
+    protonV0plusTOFGraphCut->SetName("protonV0plusTOFGraphCut");
+    for(Int_t ip = 0; ip < protonV0plusTOFGraphCut->GetN(); ip ++) {
+      Double_t mom = protonV0plusTOFGraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kProton);
+      Bool_t removePoint = mom < 0.6;
+      
+      if (removePoint) {
+        protonV0plusTOFGraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }*/
+  
+    listCollPr->Add(protonV0GraphCut);
+    //listCollPr->Add(protonV0plusTOFGraphCut);
+  }
+  else {
+    listCollPr->Add(protonTpcGraph);
+    listCollPr->Add(protonTofGraph);
+  }
+  TGraphErrors * graphPrCombined = new TGraphErrors();
+  MergeGraphErrors(graphPrCombined, listCollPr);
+  graphPrCombined->SetMarkerStyle(22);
+  graphPrCombined->SetMarkerColor(4);
+  graphPrCombined->SetNameTitle("Graph_Protons_Combined", "Graph_Protons_Combined");
+  
+  TList * listCollPi = new TList();
+  if (useV0s) {
+    //listCollPi->Add(pionTpcGraph);
+    //listCollPi->Add(pionTofGraph);
+    
+    TGraphErrors * pionV0GraphCut = new TGraphErrors(*pionV0Graph);
+    pionV0GraphCut->SetName("pionV0GraphCut");
+    for(Int_t ip = 0; ip < pionV0GraphCut->GetN(); ip ++) {
+      //Double_t mom = pionV0GraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kPion);
+      Bool_t removePoint = kFALSE;//mom >= 0.4;
+                           //|| mom < 0.2;//TODO NOW NEEDED?
+      if (removePoint) {
+        pionV0GraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }
+    /*
+    TGraphErrors * pionV0plusTOFGraphCut = new TGraphErrors(*pionV0plusTOFGraph);
+    pionV0plusTOFGraphCut->SetName("pionV0plusTOFGraphCut");
+    for(Int_t ip = 0; ip < pionV0plusTOFGraphCut->GetN(); ip ++) {
+      Double_t mom = pionV0plusTOFGraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kPion);
+      Bool_t removePoint = mom < 0.4;
+      
+      if (removePoint) {
+        pionV0plusTOFGraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }*/
+  
+    listCollPi->Add(pionV0GraphCut);
+    //listCollPi->Add(pionV0plusTOFGraphCut);
+  }
+  else {
+    listCollPi->Add(pionTpcGraph);
+    listCollPi->Add(pionTofGraph);
+  }
+  TGraphErrors * graphPiCombined = new TGraphErrors();
+  MergeGraphErrors(graphPiCombined, listCollPi);
+  graphPiCombined->SetMarkerStyle(22);
+  graphPiCombined->SetMarkerColor(3);
+  graphPiCombined->SetNameTitle("Graph_Pions_Combined", "Graph_Pions_Combined");
+  
+  TList * listCollKa = new TList();
+  listCollKa->Add(kaonTpcGraph);
+  listCollKa->Add(kaonTofGraph);
+  TGraphErrors * graphKaCombined = new TGraphErrors();
+  MergeGraphErrors(graphKaCombined, listCollKa);
+  graphKaCombined->SetMarkerStyle(22);
+  graphKaCombined->SetMarkerColor(5);
+  graphKaCombined->SetNameTitle("Graph_Kaons_Combined", "Graph_Kaons_Combined");
+  
+  TList * listCollEl = new TList();
+  if (useV0s) {
+    //listCollEl->Add(electronGraph);
+    
+    TGraphErrors * electronV0GraphCut = new TGraphErrors(*electronV0Graph);
+    electronV0GraphCut->SetName("electronV0GraphCut");
+    /*
+    for(Int_t ip = 0; ip < electronV0GraphCut->GetN(); ip ++) {
+      Double_t mom = electronV0GraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kElectron);
+      Bool_t removePoint = mom > 0.3;
+      if (removePoint) {
+        electronV0GraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }
+    
+    TGraphErrors * electronV0plusTOFGraphCut = new TGraphErrors(*electronV0plusTOFGraph);
+    electronV0plusTOFGraphCut->SetName("electronV0plusTOFGraphCut");
+    for(Int_t ip = 0; ip < electronV0plusTOFGraphCut->GetN(); ip ++) {
+      Double_t mom = electronV0plusTOFGraphCut->GetX()[ip] * AliPID::ParticleMass(AliPID::kElectron);
+      Bool_t removePoint = mom <= 0.4;
+      
+      if (removePoint) {
+        electronV0plusTOFGraphCut->RemovePoint(ip);
+        ip--;
+        continue;
+      }
+    }
+    */
+    listCollEl->Add(electronV0GraphCut);
+    //listCollEl->Add(electronV0plusTOFGraphCut);
+  }
+  else {
+    listCollEl->Add(electronGraph);
+  }
+  TGraphErrors * graphElCombined = new TGraphErrors();
+  MergeGraphErrors(graphElCombined, listCollEl);
+  graphElCombined->SetMarkerStyle(22);
+  graphElCombined->SetMarkerColor(2);
+  graphElCombined->SetNameTitle("Graph_Electrons_Combined", "Graph_Electrons_Combined");
+  
+  
+  
+  //
+  //  return array with all graphs
+  //
+  TObjArray * arrGraphs = new TObjArray();
+  arrGraphs->Add(graphAll);
+  //  
+  arrGraphs->Add(electronGraph);
+  arrGraphs->Add(electronV0Graph);
+  arrGraphs->Add(electronV0plusTOFGraph);
+  arrGraphs->Add(electronV0GraphForBBfit);
+  arrGraphs->Add(pionTpcGraph);
+  arrGraphs->Add(pionTofGraph);
+  arrGraphs->Add(pionV0Graph);
+  arrGraphs->Add(pionV0plusTOFGraph);
+  arrGraphs->Add(pionV0GraphForBBfit);
+  arrGraphs->Add(kaonTpcGraph);
+  arrGraphs->Add(kaonTofGraph);
+  arrGraphs->Add(protonTpcGraph);
+  arrGraphs->Add(protonTofGraph);
+  arrGraphs->Add(protonV0Graph);
+  arrGraphs->Add(protonV0plusTOFGraph);
+  arrGraphs->Add(protonV0GraphForBBfit);
+  //
+  arrGraphs->Add(graphElCombined);
+  arrGraphs->Add(graphPiCombined);
+  arrGraphs->Add(graphKaCombined);
+  arrGraphs->Add(graphPrCombined);
+  //
+  //
+  
+  canvasQAtpc->SaveAs("splines_QA_ResidualGraphsTPC.root");
+  canvasQAtof->SaveAs("splines_QA_ResidualGraphsTOF.root");
+  canvasQAv0->SaveAs("splines_QA_ResidualGraphsV0.root");
+  canvasQAv0plusTOF->SaveAs("splines_QA_ResidualGraphsV0plusTOF.root");
+  
+  
+  canvasQAv0DeDxPurityEl->SaveAs("V0_dEdx_purity_el.root");
+  canvasQAv0DeDxPurityPi->SaveAs("V0_dEdx_purity_Pi.root");
+  canvasQAv0DeDxPurityPr->SaveAs("V0_dEdx_purity_Pr.root");
+
+  return arrGraphs;
+
+}
+
+
+//________________________________________________________________________
+TObjArray * AliTPCcalibResidualPID::GetResidualGraphsMC(THnSparseF * histPidQA, const Char_t * /*system*/) {
+  //
+  // Extracts the residual graphs from THnSparse created from MC (NOT DATA!)
+  //
+  
+  const TString momTitle = "#it{p}_{TPC} (GeV/#it{c})";
+  const TString dEdxTitle = "d#it{E}/d#it{x} (arb. unit)";
+  
+  Int_t cutForFitting = 10;
+  Double_t heightFractionForFittingRange = 0.1; 
+  
+  //
+  THnSparse * hist = histPidQA;
+  //
+  TCanvas * canvasQAmc = new TCanvas("canvasQAmcResGraph","Control canvas for residual graphs (MC)",100,10,1380,800);
+  canvasQAmc->Divide(2,2);
+  
+  for (Int_t i = 1; i <= 4; i++) {
+    canvasQAmc->GetPad(i)->SetGrid(1, 1);
+    canvasQAmc->GetPad(i)->SetLogz();
+    canvasQAmc->GetPad(i)->SetLogx();
+  }
+  //
+  // 1. select and fit electrons
+  //
+  // In the following, axis 3 will also be limited in range, although nsigma itself is not used. This is to avoid multiple counting of entries
+  hist->GetAxis(2)->SetRangeUser(0,0); // electrons
+  hist->GetAxis(3)->SetRangeUser(0,0); // electrons (used for nsigma and for the eta correction of the signal)
+  TH2D * histElectron = hist->Projection(1,0);
+  histElectron->SetName("histElectron");
+  histElectron->RebinX(2);
+  histElectron->GetXaxis()->SetRangeUser(0.15,8.0);
+  TObjArray arr;
+  FitSlicesY(histElectron, heightFractionForFittingRange, cutForFitting, "QNRL", &arr);
+  TH1D * electronPoints = (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * electronGraph = new TGraphErrors(electronPoints);
+  electronGraph->SetName("electronGraphMC");
+  for(Int_t ip = 0; ip < electronGraph->GetN(); ip ++) {
+    Bool_t removePoint = electronGraph->GetY()[ip] < 10 || electronGraph->GetEY()[ip]/electronGraph->GetY()[ip] > 0.03
+                         || electronGraph->GetX()[ip] < 0.15;// || electronGraph->GetX()[ip] > 3.1;
+    if (removePoint) {
+      electronGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAmc->cd(1);
+  histElectron->SetTitle("electrons");
+  histElectron->GetYaxis()->SetRangeUser(50, 120);
+  histElectron->GetXaxis()->SetTitle(momTitle.Data());
+  histElectron->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histElectron->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histElectron->GetXaxis()->SetNoExponent(kTRUE);
+  histElectron->Draw("colz");
+  electronPoints->SetMarkerStyle(24);
+  electronPoints->Draw("same");
+  //
+  // 2. protons
+  //
+  hist->GetAxis(2)->SetRangeUser(3,3); // protons
+  hist->GetAxis(3)->SetRangeUser(3,3); // protons (used for nsigma and for the eta correction of the signal)
+  //
+  TH2D * histProton = hist->Projection(1,0);
+  histProton->SetName("histProton");
+  histProton->GetXaxis()->SetRangeUser(0.15,8);
+  histProton->GetYaxis()->SetRangeUser(10, hist->GetAxis(1)->GetBinUpEdge(hist->GetAxis(1)->GetNbins()));
+  FitSlicesY(histProton, heightFractionForFittingRange, cutForFitting, "QNRL", &arr);
+  TH1D * protonPoints = (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * protonGraph = new TGraphErrors(protonPoints);
+  protonGraph->SetName("protonGraphMC");
+  for(Int_t ip = 0; ip < protonGraph->GetN(); ip ++) {
+    Bool_t removePoint = protonGraph->GetY()[ip] < 10 || protonGraph->GetEY()[ip]/protonGraph->GetY()[ip] > 0.02 || 
+                         protonGraph->GetX()[ip] < 0.15 || protonGraph->GetX()[ip] > 6;
+    if (removePoint) {
+      protonGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAmc->cd(2);
+  histProton->SetTitle("protons");
+  histProton->GetXaxis()->SetTitle(momTitle.Data());
+  histProton->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histProton->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histProton->GetXaxis()->SetNoExponent(kTRUE);
+  histProton->Draw("colz");
+  protonPoints->SetMarkerStyle(20);
+  protonPoints->Draw("same");
+  
+  //
+  // 3. pions
+  //
+  hist->GetAxis(2)->SetRangeUser(1,1); // pions
+  hist->GetAxis(3)->SetRangeUser(1,1); // pions (used for nsigma and for the eta correction of the signal)
+  //
+  TH2D * histPion = hist->Projection(1,0);
+  histPion->SetName("histPion");
+  histPion->GetXaxis()->SetRangeUser(0.15,50);
+  FitSlicesY(histPion, heightFractionForFittingRange, cutForFitting, "QNRL", &arr);
+  TH1D * pionPoints =  (TH1D *) arr.At(1)->Clone();
+  //
+  TGraphErrors * pionGraph = new TGraphErrors(pionPoints);
+  pionGraph->SetName("pionGraphMC");
+  for(Int_t ip = 0; ip < pionGraph->GetN(); ip ++) {
+    Bool_t removePoint = pionGraph->GetY()[ip] < 10 || pionGraph->GetEY()[ip]/pionGraph->GetY()[ip] > 0.005
+                         || pionGraph->GetX()[ip] < 0.15 || pionGraph->GetX()[ip] > 30;
+    if (removePoint) {
+      pionGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAmc->cd(3);
+  histPion->GetYaxis()->SetRangeUser(30, 90);
+  histPion->GetXaxis()->SetTitle(momTitle.Data());
+  histPion->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histPion->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histPion->GetXaxis()->SetNoExponent(kTRUE);
+  histPion->Draw("colz");
+  histPion->SetTitle("pions");
+  pionPoints->SetMarkerStyle(20);
+  pionPoints->Draw("same");
+  
+  //
+  // 4. kaons
+  //
+  hist->GetAxis(2)->SetRangeUser(2,2); // kaons
+  hist->GetAxis(3)->SetRangeUser(2,2); // kaons (used for nsigma and for the eta correction of the signal)
+  //
+  TH2D * histKaon = hist->Projection(1,0);
+  histKaon->SetName("histKaon");
+  histKaon->GetXaxis()->SetRangeUser(0.15,8);
+  FitSlicesY(histKaon, heightFractionForFittingRange, cutForFitting, "QNRL", &arr);
+  TH1D * kaonPoints = (TH1D*) arr.At(1)->Clone();
+  //
+  TGraphErrors * kaonGraph = new TGraphErrors(kaonPoints);
+  kaonGraph->SetName("kaonGraphMC");
+  for(Int_t ip = 0; ip < kaonGraph->GetN(); ip ++) {
+    Bool_t removePoint = kaonGraph->GetY()[ip] < 10 || kaonGraph->GetEY()[ip]/kaonGraph->GetY()[ip] > 0.02
+                         || kaonGraph->GetX()[ip] < 0.15;
+    if (removePoint) {
+      kaonGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  canvasQAmc->cd(4);
+  histKaon->SetTitle("kaons");
+  histKaon->GetYaxis()->SetRangeUser(30, 500);
+  histKaon->GetXaxis()->SetTitle(momTitle.Data());
+  histKaon->GetYaxis()->SetTitle(dEdxTitle.Data());
+  histKaon->GetXaxis()->SetMoreLogLabels(kTRUE);
+  histKaon->GetXaxis()->SetNoExponent(kTRUE);
+  histKaon->Draw("colz");
+  kaonPoints->SetMarkerStyle(20);
+  kaonPoints->Draw("same");
+  
+  
+  
+  hist->GetAxis(2)->SetRange(0,-1); // RESET RANGES
+  hist->GetAxis(3)->SetRange(0,-1); // RESET RANGES
+  
+  
+  // Clone graphs to have graphs with the same names as for the data case -> same subsequent functions can be used to determine
+  // the correction functions and, finally, the response functions.
+  // In addition: Remove some points, which are expected to deviate from the Bethe-Bloch curve, from the original graphs, so that
+  // these graphs can be used for the BB fit
+  TGraphErrors * graphPrCombined = new TGraphErrors(*protonGraph);
+  graphPrCombined->SetMarkerStyle(22);
+  graphPrCombined->SetMarkerColor(4);
+  graphPrCombined->SetNameTitle("Graph_Protons_Combined", "Graph_Protons_Combined");
+  
+  TGraphErrors * graphPiCombined = new TGraphErrors(*pionGraph);
+  graphPiCombined->SetMarkerStyle(22);
+  graphPiCombined->SetMarkerColor(3);
+  graphPiCombined->SetNameTitle("Graph_Pions_Combined", "Graph_Pions_Combined");
+  
+  TGraphErrors * graphKaCombined = new TGraphErrors(*kaonGraph);
+  graphKaCombined->SetMarkerStyle(22);
+  graphKaCombined->SetMarkerColor(5);
+  graphKaCombined->SetNameTitle("Graph_Kaons_Combined", "Graph_Kaons_Combined");
+  
+  TGraphErrors * graphElCombined = new TGraphErrors(*electronGraph);
+  graphElCombined->SetMarkerStyle(22);
+  graphElCombined->SetMarkerColor(2);
+  graphElCombined->SetNameTitle("Graph_Electrons_Combined", "Graph_Electrons_Combined");
+  
+  for(Int_t ip = 0; ip < electronGraph->GetN(); ip ++) {
+    Bool_t removePoint = electronGraph->GetX()[ip] < 2.5;// || electronGraph->GetX()[ip] > 5.0;
+    //Bool_t removePoint = electronGraph->GetX()[ip] < 1.2 || electronGraph->GetX()[ip] > 5.0;
+    if (removePoint) {
+      electronGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  for(Int_t ip = 0; ip < protonGraph->GetN(); ip ++) {
+    Bool_t removePoint = protonGraph->GetX()[ip] < 0.9
+                         || protonGraph->GetX()[ip] > 2.5;//3.5; //TODO NEW in order not to get into conflict with pions/kaons
+    if (removePoint) {
+      protonGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  for(Int_t ip = 0; ip < pionGraph->GetN(); ip ++) {
+    Bool_t removePoint = pionGraph->GetX()[ip] < 2.1;//TODO APR18 0.8;
+    if (removePoint) {
+      pionGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  
+  for(Int_t ip = 0; ip < kaonGraph->GetN(); ip ++) {
+    Bool_t removePoint = kaonGraph->GetX()[ip] < 1.25 || kaonGraph->GetX()[ip] > 4.0;// < 0.7;// || kaonGraph->GetX()[ip] > 4;
+    if (removePoint) {
+      kaonGraph->RemovePoint(ip);
+      ip--;
+      continue;
+    }
+  }
+  //
+  // rescale graphs to betaGamma
+  //
+  kaonGraph->SetMarkerStyle(22);
+  for(Int_t ip = 0; ip < kaonGraph->GetN(); ip ++) {
+    kaonGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+    kaonGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+  }
+  for(Int_t ip = 0; ip < graphKaCombined->GetN(); ip ++) {
+    graphKaCombined->GetX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+    graphKaCombined->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kKaon);
+  }
+  //
+  electronGraph->SetMarkerStyle(22);
+  electronGraph->SetMarkerColor(2);
+  for(Int_t ip = 0; ip < electronGraph->GetN(); ip ++) {
+    electronGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    electronGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  for(Int_t ip = 0; ip < graphElCombined->GetN(); ip ++) {
+    graphElCombined->GetX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+    graphElCombined->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kElectron);
+  }
+  //
+  pionGraph->SetMarkerStyle(22);
+  pionGraph->SetMarkerColor(3);
+  for(Int_t ip = 0; ip < pionGraph->GetN(); ip ++) {
+    pionGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    pionGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  for(Int_t ip = 0; ip < graphPiCombined->GetN(); ip ++) {
+    graphPiCombined->GetX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+    graphPiCombined->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kPion);
+  }
+  //
+  protonGraph->SetMarkerStyle(22);
+  protonGraph->SetMarkerColor(4);
+  for(Int_t ip = 0; ip < protonGraph->GetN(); ip ++) {
+    protonGraph->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    protonGraph->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  for(Int_t ip = 0; ip < graphPrCombined->GetN(); ip ++) {
+    graphPrCombined->GetX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+    graphPrCombined->GetEX()[ip] /= AliPID::ParticleMass(AliPID::kProton);
+  }
+  //
+  //
+  
+  // Store graphs without further restriction
+  TGraphErrors * graphPrAll = new TGraphErrors(*graphPrCombined);
+  graphPrAll->SetNameTitle("Protons_MC_all", "Protons_MC_all");
+  
+  TGraphErrors * graphPiAll = new TGraphErrors(*graphPiCombined);
+  graphPiAll->SetNameTitle("Pions_MC_all", "Pions_MC_all");
+  
+  TGraphErrors * graphKaAll = new TGraphErrors(*graphKaCombined);
+  graphKaAll->SetNameTitle("Kaons_MC_all", "Kaons_MC_all");
+  
+  TGraphErrors * graphElAll = new TGraphErrors(*graphElCombined);
+  graphElAll->SetNameTitle("Electrons_MC_all", "Electrons_MC_all");
+  
+  //
+  //
+  TGraphErrors * graphAll = new TGraphErrors();
+  TList * listColl = new TList();
+  listColl->Add(electronGraph);
+  listColl->Add(protonGraph); 
+  listColl->Add(kaonGraph);
+  listColl->Add(pionGraph);
+  MergeGraphErrors(graphAll, listColl);
+  graphAll->SetNameTitle("beamDataPoints","beamDataPoints");
+  
+  //
+  //  return array with all graphs
+  //
+  TObjArray * arrGraphs = new TObjArray();
+  arrGraphs->Add(graphAll);
+  //  
+  arrGraphs->Add(electronGraph);
+  arrGraphs->Add(pionGraph);
+  arrGraphs->Add(kaonGraph);
+  arrGraphs->Add(protonGraph);
+  //
+  arrGraphs->Add(graphElAll);
+  arrGraphs->Add(graphPiAll);
+  arrGraphs->Add(graphKaAll);
+  arrGraphs->Add(graphPrAll);  
+  //
+  arrGraphs->Add(graphElCombined);
+  arrGraphs->Add(graphPiCombined);
+  arrGraphs->Add(graphKaCombined);
+  arrGraphs->Add(graphPrCombined);
+  //
+  //
+  
+  canvasQAmc->SaveAs("splines_QA_ResidualGraphsTPC.root");
+
+  return arrGraphs;
+
+}
+
+
+//________________________________________________________________________
+TObjArray * AliTPCcalibResidualPID::GetResponseFunctions(TF1* parametrisation, TObjArray* inputGraphs, const Char_t * type, const Char_t * period, const Char_t * pass, const Char_t * system, const Char_t* dedxtype) {
+  //
+  //
+  
+  Bool_t isMC = kFALSE;
+  if (strcmp(type, "MC") == 0) {
+    isMC = kTRUE;
+  }
+  else if (strcmp(type, "DATA") == 0) {
+    isMC = kFALSE;
+  }
+  else {
+    Printf("ERROR - GetResponseFunctions: Unknown type \"%s\" - must be \"MC\" or \"DATA\"!", type);
+    
+    return 0x0;
+  }
+  
+  Bool_t isPbPb = kFALSE;
+  Bool_t isPPb = kFALSE;
+  if (strcmp(system, "PBPB") == 0) {
+    Printf("PbPb detected - Applying special handling!");
+    isPbPb = kTRUE;
+  }
+  else if (strcmp(system, "PPB") == 0 || strcmp(system, "PBP") == 0) {
+    Printf("p-Pb/Pb-p detected - Applying special handling!");
+    isPPb = kTRUE;
+  }
+  
+  TCanvas* canvasQA[4] = { 0x0, };
+  for (Int_t i = 0; i < 4; i++) {
+    canvasQA[i] = new TCanvas(Form("canvasQAres_%d", i), "Control canvas for residual polynomial",100,10,1380,800);
+    canvasQA[i]->SetGrid(1, 1);
+    canvasQA[i]->SetLogx();
+  }
+  /*
+  TCanvas * canvasQA = new TCanvas("canvasQAres","Control canvas for residual polynomial",100,10,1380,800);
+  canvasQA->Divide(2,2);
+  
+  for (Int_t i = 1; i <= 4; i++) {
+    canvasQA->GetPad(i)->SetGrid(1, 1);
+  }
+  */
+  //
+  // smallest needed (100MeV proton) bg = 0.1/0.938(=AliPID::ParticleMass(AliPID::kProton)) = 0.1, 
+  // largest needed 100 GeV electron, bg = 100/0.000511(=AliPID::ParticleMass(AliPID::kElectron)) = 200 000
+  //
+  Double_t from = 0.1;
+  Double_t to = 200000;
+  
+  TF1* funcBB = new TF1(*parametrisation);
+  funcBB->SetLineColor(2);
+  funcBB->SetLineWidth(1);
+  
+  TString fitFuncString = "[0] + [1] / x + [2] / (x**2) + [3] / (x**3) + [4] / (x**4) + [5] / (x**5) + [6] * x";
+  TString fitFuncString2 = "pol6";
+  //
+  // 1. extract proton corrections
+  //
+  TGraphErrors *  graphProtonTPC = (TGraphErrors *) inputGraphs->FindObject("Graph_Protons_Combined");
+  TGraphErrors *  graphProtonTPCfinal = new TGraphErrors(*graphProtonTPC);
+  graphProtonTPCfinal->SetName("graphProtonTPCfinal");
+  for(Int_t ip = 0; ip < graphProtonTPC->GetN(); ip ++) {
+    graphProtonTPC->GetY()[ip] -= funcBB->Eval(graphProtonTPC->GetX()[ip]);
+    graphProtonTPC->GetY()[ip] /= funcBB->Eval(graphProtonTPC->GetX()[ip]);
+    graphProtonTPC->GetEY()[ip] /= funcBB->Eval(graphProtonTPC->GetX()[ip]);;
+  }
+  canvasQA[0]->cd();
+  //canvasQA->cd(1);
+  //gPad->SetLogx();
+  graphProtonTPC->GetXaxis()->SetTitle("#beta#gamma");
+  graphProtonTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  graphProtonTPC->GetXaxis()->SetNoExponent(kTRUE);
+  graphProtonTPC->GetYaxis()->SetLabelSize(0.04);
+  graphProtonTPC->GetYaxis()->SetTitleOffset(0.85);
+  graphProtonTPC->GetXaxis()->SetTitleOffset(1.0);
+  graphProtonTPC->GetYaxis()->SetTitle("(data - fit) / fit");
+  graphProtonTPC->Draw("ap");
+  
+  if (graphProtonTPC->GetN() <= 0)  {
+    Printf("ERROR - GetResponseFunctions: Proton graph has no data points!");
+    
+    return 0x0;
+  }
+  graphProtonTPC->Sort(); // Sort points along x. Next, the very first point will be used to determine the starting point of the correction function
+  TF1 * funcCorrProton = new TF1("funcCorrProton", fitFuncString2.Data(), TMath::Max(graphProtonTPC->GetX()[0], 0.15),1.0); // TODO BEN was 0.18 - 0.85
+  graphProtonTPC->Fit(funcCorrProton, "QREX0M");
+  //
+  // 2. extract kaon corrections
+  //
+  TGraphErrors *  graphKaonTPC = (TGraphErrors *) inputGraphs->FindObject("Graph_Kaons_Combined");
+  TGraphErrors *  graphKaonTPCfinal = new TGraphErrors(*graphKaonTPC);
+  graphKaonTPCfinal->SetName("graphKaonTPCfinal");
+  for(Int_t ip = 0; ip < graphKaonTPC->GetN(); ip ++) {
+    graphKaonTPC->GetY()[ip] -= funcBB->Eval(graphKaonTPC->GetX()[ip]);
+    graphKaonTPC->GetY()[ip] /= funcBB->Eval(graphKaonTPC->GetX()[ip]);
+    graphKaonTPC->GetEY()[ip] /= funcBB->Eval(graphKaonTPC->GetX()[ip]);;
+  }
+  canvasQA[1]->cd();
+  //canvasQA->cd(2);
+  //gPad->SetLogx();
+  graphKaonTPC->GetXaxis()->SetTitle("#beta#gamma");
+  graphKaonTPC->GetYaxis()->SetTitle("(data - fit) / fit");
+  graphKaonTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  graphKaonTPC->GetXaxis()->SetNoExponent(kTRUE);
+  graphKaonTPC->GetYaxis()->SetLabelSize(0.04);
+  graphKaonTPC->GetYaxis()->SetTitleOffset(0.85);
+  graphKaonTPC->GetXaxis()->SetTitleOffset(1.0);
+  graphKaonTPC->Draw("ap");
+  
+  if (graphKaonTPC->GetN() <= 0)  {
+    Printf("ERROR - GetResponseFunctions: Kaon graph has no data points!");
+    
+    return 0x0;
+  }
+  graphKaonTPC->Sort(); // Sort points along x. Next, the very first point will be used to determine the starting point of the correction function
+  
+  TF1 * funcCorrKaon = 0x0;
+  
+  if (isMC) {
+    funcCorrKaon = new TF1("funcCorrKaon", fitFuncString.Data(), TMath::Max(graphKaonTPC->GetX()[0], 0.25), isPPb ? 3.44 : 1.981);
+    graphKaonTPC->Fit(funcCorrKaon, "QREX0M");
+  }
+  else {
+    // In case of data there are sometimes problems to fit the shape with one function (could describe the overall deviation,
+    // but will not cover all the details).
+    // Nevertheless, this shape (including most of the "details") can be fitted with the following approach with two functions
+    TF1 * funcCorrKaon1 = new TF1("funcCorrKaon1", fitFuncString.Data(),
+                                  TMath::Max(graphKaonTPC->GetX()[0], 0.25), 1.981); 
+    graphKaonTPC->Fit(funcCorrKaon1, "QREX0M", "same", TMath::Max(graphKaonTPC->GetX()[0], 0.25), 1.0);
+    
+    TF1 * funcCorrKaon2 = new TF1("funcCorrKaon2", fitFuncString2.Data(), TMath::Max(graphKaonTPC->GetX()[0], 0.25),  1.981);
+    graphKaonTPC->Fit(funcCorrKaon2, "QREX0M", "same", (isMC ? 1.981 : 1.0), 1.981);
+    
+    funcCorrKaon = new TF1("funcCorrKaon",
+                           "funcCorrKaon1 * 0.5*(1.+TMath::Erf((1 - x) / 0.1)) + ([0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x+[5]*x*x*x*x*x+[6]*x*x*x*x*x*x) * 0.5*(1.+TMath::Erf((x - 1) / 0.1))",
+                           TMath::Max(graphKaonTPC->GetX()[0], 0.25), 1.981);
+  
+    for (Int_t i = funcCorrKaon1->GetNpar(), j = 0; i < funcCorrKaon1->GetNpar() + funcCorrKaon2->GetNpar(); i++, j++) {
+      funcCorrKaon->SetParameter(j, funcCorrKaon2->GetParameter(j));
+    }
+    funcCorrKaon->SetLineColor(kRed);
+    funcCorrKaon->GetHistogram()->DrawClone("csame");
+    //funcCorrKaon->Draw("same");
+  }
+  /*TODO
+  TF1 * funcCorrKaon = new TF1("funcCorrKaon", fitFuncString.Data(),//TODO BEN was fitFuncString2
+                               TMath::Max(graphKaonTPC->GetX()[0], 0.25), (isMC ? 1.981 : 1.0)); //TODO BEN was 0.79 for data and 1.45 for MC
+  graphKaonTPC->Fit(funcCorrKaon, "QREX0M");
+  */
+  //
+  // 3. extract pion corrections
+  //
+  TGraphErrors *  graphPionTPC = (TGraphErrors *)  inputGraphs->FindObject("Graph_Pions_Combined");
+  TGraphErrors *  graphPionTPCfinal = new TGraphErrors(*graphPionTPC);
+  graphPionTPCfinal->SetName("graphPionTPCfinal");
+  for(Int_t ip = 0; ip < graphPionTPC->GetN(); ip ++) {
+    graphPionTPC->GetY()[ip] -= funcBB->Eval(graphPionTPC->GetX()[ip]);
+    graphPionTPC->GetY()[ip] /= funcBB->Eval(graphPionTPC->GetX()[ip]);
+    graphPionTPC->GetEY()[ip] /= funcBB->Eval(graphPionTPC->GetX()[ip]);
+  }
+  canvasQA[2]->cd();
+  //canvasQA->cd(3);
+  //gPad->SetLogx();
+  graphPionTPC->GetXaxis()->SetTitle("#beta#gamma");
+  graphPionTPC->GetYaxis()->SetTitle("(data - fit) / fit");
+  graphPionTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  graphPionTPC->GetXaxis()->SetNoExponent(kTRUE);
+  graphPionTPC->GetYaxis()->SetLabelSize(0.04);
+  graphPionTPC->GetYaxis()->SetTitleOffset(0.85);
+  graphPionTPC->GetXaxis()->SetTitleOffset(1.0);
+  graphPionTPC->Draw("ap");
+  
+  if (graphPionTPC->GetN() <= 0)  {
+    Printf("ERROR - GetResponseFunctions: Pion graph has no data points!");
+    
+    return 0x0;
+  }
+  graphPionTPC->Sort(); // Sort points along x. Next, the very first point will be used to determine the starting point of the correction function
+  // In case of PbPb (data, not MC) only correct down to 0.2 GeV/c; otherwise: contamination due to electrons
+  TF1 * funcCorrPion = new TF1("funcCorrPion", fitFuncString.Data(), ((isPbPb && !isMC) ? (0.2 / AliPID::ParticleMass(AliPID::kPion)) : graphPionTPC->GetX()[0]), isMC ? (isPPb ? 12.8 : (isPbPb ? 12.8 : 7.1)) : (isPPb ? 7.68 : 7.1)); 
+  graphPionTPC->Fit(funcCorrPion, "QREX0M");
+  //
+  // 4. extract electron corrections
+  //
+  TGraphErrors *  graphElectronTPC = (TGraphErrors *)  inputGraphs->FindObject("Graph_Electrons_Combined");
+  TGraphErrors *  graphElectronTPCfinal = new TGraphErrors(*graphElectronTPC);
+  graphElectronTPCfinal->SetName("graphElectronTPCfinal");
+  for(Int_t ip = 0; ip < graphElectronTPC->GetN(); ip ++) {
+    graphElectronTPC->GetY()[ip] -= funcBB->Eval(graphElectronTPC->GetX()[ip]);
+    graphElectronTPC->GetY()[ip] /= funcBB->Eval(graphElectronTPC->GetX()[ip]);
+    graphElectronTPC->GetEY()[ip] /= funcBB->Eval(graphElectronTPC->GetX()[ip]);;
+  }
+  canvasQA[3]->cd();
+  //canvasQA->cd(4);
+  //gPad->SetLogx();
+  graphElectronTPC->GetXaxis()->SetTitle("#beta#gamma");
+  graphElectronTPC->GetYaxis()->SetTitle("(data - fit) / fit");
+  graphElectronTPC->GetXaxis()->SetMoreLogLabels(kTRUE);
+  graphElectronTPC->GetXaxis()->SetNoExponent(kTRUE);
+  graphElectronTPC->GetYaxis()->SetLabelSize(0.04);
+  graphElectronTPC->GetYaxis()->SetTitleOffset(0.85);
+  graphElectronTPC->GetXaxis()->SetTitleOffset(1.0);
+  graphElectronTPC->Draw("ap");
+  
+  if (graphElectronTPC->GetN() <= 0)  {
+    Printf("ERROR - GetResponseFunctions: Electron graph has no data points!");
+    
+    return 0x0;
+  }
+  graphElectronTPC->Sort(); // Sort points along x. Next, the very first point will be used to determine the starting point of the correction function
+  // In case of PbPb (data, not MC) only correct down to 0.2 GeV/c; otherwise: contamination due to pions
+  TF1 * funcCorrElectron = new TF1("funcCorrElectron", fitFuncString.Data(), (!isMC && isPbPb ? (0.2 / AliPID::ParticleMass(AliPID::kElectron)) :graphElectronTPC->GetX()[0]), (isMC ? 3565 : (isPPb ? 2900 : 1920/*970*/)));// TODO was 1800 for pp data
+  // NOTE: For data, the results are almost the same for fitFuncString and fitFuncString2. Maybe, fitFuncString2 is slightly better.
+  graphElectronTPC->Fit(funcCorrElectron, "QREX0M");
+  //
+  // EXTRACT GRAPHS AND PUT THEM TO THE TOBJARRAY
+  //
+  const Int_t nBins = 500;
+  Double_t xBetaGamma[nBins];
+  Double_t yProton[nBins];
+  Double_t yKaon[nBins];
+  Double_t yPion[nBins];
+  Double_t yElectron[nBins];
+  Double_t yDefault[nBins];
+  //
+  // 
+  //
+  xBetaGamma[0] = from;
+  Double_t factor = pow(to/from, 1./nBins);
+  
+  //
+  for(Int_t kk = 0; kk < nBins; kk++) {
+    if (kk > 0) xBetaGamma[kk] = factor * xBetaGamma[kk-1];
+    yProton[kk] =  funcBB->Eval(xBetaGamma[kk])/50.;
+    yPion[kk] =  funcBB->Eval(xBetaGamma[kk])/50.;
+    yKaon[kk] =  funcBB->Eval(xBetaGamma[kk])/50.;
+    yElectron[kk] =  funcBB->Eval(xBetaGamma[kk])/50.;
+    yDefault[kk] = funcBB->Eval(xBetaGamma[kk])/50.;
+
+    // Added by Ben
+    Double_t widthFactor = 0.020;
+    Double_t smoothProton   = 0.5*(TMath::Erf((funcCorrProton->GetXmax()-xBetaGamma[kk])*AliPID::ParticleMass(AliPID::kProton)/widthFactor) + 1);
+    Double_t smoothKaon     = 0.5*(TMath::Erf((funcCorrKaon->GetXmax()-xBetaGamma[kk])*AliPID::ParticleMass(AliPID::kKaon)/widthFactor) + 1);
+    Double_t smoothPion     = 0.5*(TMath::Erf((funcCorrPion->GetXmax()-xBetaGamma[kk])*AliPID::ParticleMass(AliPID::kPion)/widthFactor) + 1);
+    Double_t smoothElectron = 0.5*(TMath::Erf((funcCorrElectron->GetXmax()-xBetaGamma[kk])*AliPID::ParticleMass(AliPID::kElectron)/widthFactor) + 1);
+
+    if (xBetaGamma[kk] > funcCorrProton->GetXmax())
+      yProton[kk] *= (1 + smoothProton*funcCorrProton->Eval(funcCorrProton->GetXmax()));
+    // Correction is so large that one runs into trouble at low bg for Protons.
+    // The deviation is smaller if one takes the lower bound of the correction function
+    else if (xBetaGamma[kk] < funcCorrProton->GetXmin())
+      yProton[kk] *= (1 + smoothProton*funcCorrProton->Eval(funcCorrProton->GetXmin()));
+    else
+      yProton[kk] *= (1 + smoothProton*funcCorrProton->Eval(xBetaGamma[kk]));
+    
+    if (xBetaGamma[kk] > funcCorrKaon->GetXmax())
+      yKaon[kk] *= (1 + smoothKaon*funcCorrKaon->Eval(funcCorrKaon->GetXmax()));
+    // Correction is so large that one runs into trouble at low bg for Kaons.
+    // The deviation is smaller if one takes the lower bound of the correction function
+    else if (xBetaGamma[kk] < funcCorrKaon->GetXmin())
+      yKaon[kk] *= (1 + smoothKaon*funcCorrKaon->Eval(funcCorrKaon->GetXmin()));
+    else
+      yKaon[kk] *= (1 + smoothKaon*funcCorrKaon->Eval(xBetaGamma[kk]));
+
+    if (xBetaGamma[kk] > funcCorrElectron->GetXmax())
+      yElectron[kk] *= (1 + smoothElectron*funcCorrElectron->Eval(funcCorrElectron->GetXmax()));
+    else if (xBetaGamma[kk] < funcCorrElectron->GetXmin())
+      yElectron[kk] *= (1 + smoothElectron*funcCorrElectron->Eval(funcCorrElectron->GetXmin()));
+    else
+      yElectron[kk] *= (1 + smoothElectron*funcCorrElectron->Eval(xBetaGamma[kk]));
+    
+    // Only true for LHC10d.pass?: Seems not to be needed because any (very small) deviations are most likely due to electron contamination(BEN)
+    if (xBetaGamma[kk] > funcCorrPion->GetXmax())
+      yPion[kk] *= (1 + smoothPion*funcCorrPion->Eval(funcCorrPion->GetXmax()));
+    else if (xBetaGamma[kk] < funcCorrPion->GetXmin())
+      yPion[kk] *= (1 + smoothPion*funcCorrPion->Eval(funcCorrPion->GetXmin()));
+    else
+      yPion[kk] *= (1 + smoothPion*funcCorrPion->Eval(xBetaGamma[kk]));
+
+    
+    /* Removed by Ben
+    Double_t smoothProton = 0.5*(TMath::Erf((funcCorrProton->GetXmax()-xBetaGamma[kk])/0.002) + 1);
+    Double_t smoothKaon   = 0.5*(TMath::Erf((funcCorrKaon->GetXmax()-xBetaGamma[kk])/0.002) + 1);
+    Double_t smoothPion   = 0.5*(TMath::Erf((funcCorrPion->GetXmax()-xBetaGamma[kk])/0.002) + 1);
+
+    if (xBetaGamma[kk] < funcCorrProton->GetXmax() && xBetaGamma[kk] > funcCorrProton->GetXmin()) yProton[kk] *= (1 + smoothProton*funcCorrProton->Eval(xBetaGamma[kk])); 
+    if (xBetaGamma[kk] < funcCorrKaon->GetXmax() && xBetaGamma[kk] > funcCorrKaon->GetXmin()) yKaon[kk] *= (1 + smoothKaon*funcCorrKaon->Eval(xBetaGamma[kk])); 
+    if (xBetaGamma[kk] < funcCorrPion->GetXmax() && xBetaGamma[kk] > funcCorrPion->GetXmin()) yPion[kk] *= (1 + smoothPion*funcCorrPion->Eval(xBetaGamma[kk])); 
+    //if (xBetaGamma[kk] < funcCorrElectron->GetXmax()) yElectron[kk] *= (1 + funcCorrElectron->Eval(xBetaGamma[kk])); 
+    //
+    if (xBetaGamma[kk] < funcCorrProton->GetXmin()) yProton[kk] *= (1 + funcCorrProton->Eval(funcCorrProton->GetXmin())); 
+    if (xBetaGamma[kk] < funcCorrKaon->GetXmin()) yKaon[kk] *= (1 + funcCorrKaon->Eval(funcCorrKaon->GetXmin())); 
+    if (xBetaGamma[kk] < funcCorrPion->GetXmin()) yPion[kk] *= (1 + funcCorrPion->Eval(funcCorrPion->GetXmin())); 
+    */
+  }
+  //
+  TGraph * graphProton = new TGraph(nBins, xBetaGamma, yProton);
+  TGraph * graphElectron = new TGraph(nBins, xBetaGamma, yElectron);
+  TGraph * graphKaon = new TGraph(nBins, xBetaGamma, yKaon);
+  TGraph * graphPion = new TGraph(nBins, xBetaGamma, yPion);
+  TGraph * graphDefault = new TGraph(nBins, xBetaGamma, yDefault);
+  //
+  //
+  TSpline3 * splineProton = new TSpline3("splineProton", graphProton);
+  TSpline3 * splineElectron = new TSpline3("splineElectron", graphElectron);
+  TSpline3 * splineKaon = new TSpline3("splineKaon", graphKaon);
+  TSpline3 * splinePion = new TSpline3("splinePion", graphPion);
+  TSpline3 * splineDefault = new TSpline3("splineDefault", graphDefault);
+  //
+  //
+  splineProton->SetNameTitle(Form("TSPLINE3_%s_PROTON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype),
+                             Form("TSPLINE3_%s_PROTON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype));
+  splineElectron->SetNameTitle(Form("TSPLINE3_%s_ELECTRON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype),
+                               Form("TSPLINE3_%s_ELECTRON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype));
+  splineKaon->SetNameTitle(Form("TSPLINE3_%s_KAON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype),
+                           Form("TSPLINE3_%s_KAON_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype));
+  splinePion->SetNameTitle(Form("TSPLINE3_%s_PION_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype),
+                           Form("TSPLINE3_%s_PION_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype));
+  splineDefault->SetNameTitle(Form("TSPLINE3_%s_ALL_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype),
+                              Form("TSPLINE3_%s_ALL_%s_%s_%s_%sMEAN",type,period,pass,system,dedxtype));
+  //
+  TObjArray * arrResponse = new TObjArray();
+  arrResponse->SetName("TPCpidResponseFunctions");
+  arrResponse->AddLast(splineProton);
+  arrResponse->AddLast(splineElectron);
+  arrResponse->AddLast(splineKaon);
+  arrResponse->AddLast(splinePion);
+  arrResponse->AddLast(splineDefault);
+  //
+  
+  // Draw deviation from final results
+  for(Int_t ip = 0; ip < graphProtonTPCfinal->GetN(); ip ++) {
+    graphProtonTPCfinal->GetY()[ip] -= 50. * splineProton->Eval(graphProtonTPCfinal->GetX()[ip]);
+    graphProtonTPCfinal->GetY()[ip] /= 50. * splineProton->Eval(graphProtonTPCfinal->GetX()[ip]);
+    graphProtonTPCfinal->GetEY()[ip] /= 50. * splineProton->Eval(graphProtonTPCfinal->GetX()[ip]);;
+  }
+  canvasQA[0]->cd();
+  //canvasQA->cd(1);
+  graphProtonTPCfinal->SetMarkerStyle(26);
+  graphProtonTPCfinal->Draw("psame");
+  
+  for(Int_t ip = 0; ip < graphKaonTPCfinal->GetN(); ip ++) {
+    graphKaonTPCfinal->GetY()[ip] -= 50. * splineKaon->Eval(graphKaonTPCfinal->GetX()[ip]);
+    graphKaonTPCfinal->GetY()[ip] /= 50. * splineKaon->Eval(graphKaonTPCfinal->GetX()[ip]);
+    graphKaonTPCfinal->GetEY()[ip] /= 50. * splineKaon->Eval(graphKaonTPCfinal->GetX()[ip]);;
+  }
+  canvasQA[1]->cd();
+  //canvasQA->cd(2);
+  graphKaonTPCfinal->SetMarkerStyle(26);
+  graphKaonTPCfinal->Draw("psame");
+  
+  for(Int_t ip = 0; ip < graphPionTPCfinal->GetN(); ip ++) {
+    graphPionTPCfinal->GetY()[ip] -= 50. * splinePion->Eval(graphPionTPCfinal->GetX()[ip]);
+    graphPionTPCfinal->GetY()[ip] /= 50. * splinePion->Eval(graphPionTPCfinal->GetX()[ip]);
+    graphPionTPCfinal->GetEY()[ip] /= 50. * splinePion->Eval(graphPionTPCfinal->GetX()[ip]);;
+  }
+  canvasQA[2]->cd();
+  //canvasQA->cd(3);
+  graphPionTPCfinal->SetMarkerStyle(26);
+  graphPionTPCfinal->Draw("psame");
+  
+  for(Int_t ip = 0; ip < graphElectronTPCfinal->GetN(); ip ++) {
+    graphElectronTPCfinal->GetY()[ip] -= 50. * splineElectron->Eval(graphElectronTPCfinal->GetX()[ip]);
+    graphElectronTPCfinal->GetY()[ip] /= 50. * splineElectron->Eval(graphElectronTPCfinal->GetX()[ip]);
+    graphElectronTPCfinal->GetEY()[ip] /= 50. * splineElectron->Eval(graphElectronTPCfinal->GetX()[ip]);;
+  }
+  canvasQA[3]->cd();
+  //canvasQA->cd(4);
+  graphElectronTPCfinal->SetMarkerStyle(26);
+  graphElectronTPCfinal->Draw("psame");
+  
+  TFile* fSave = TFile::Open("splines_QA_ResidualPolynomials.root", "RECREATE");
+  fSave->cd();
+  for (Int_t i = 0; i < 4; i++)
+    canvasQA[i]->Write();  
+  
+  fSave->Close();
+  //canvasQA->SaveAs("splines_QA_ResidualPolynomials.root");
+  
+  
+  delete funcBB;
+  
+  return arrResponse;
+}
+
+
+//________________________________________________________________________
+TF1* AliTPCcalibResidualPID::FitBB(TObjArray* inputGraphs, Bool_t isMC, Bool_t isPPb, const Bool_t useV0s, 
+                                   const Double_t * initialParameters, AliTPCcalibResidualPID::FitType fitType) {
+  //
+  // Fit Bethe-Bloch parametrisation to data points (inputGraphs)
+  //
+  const Int_t nPar = 6;
+  TGraphErrors * graphAll = (TGraphErrors *) inputGraphs->FindObject("beamDataPoints");
+  //
+  Float_t from = 0.9; //TODO ADJUST -> Very important
+  Float_t to = graphAll->GetXaxis()->GetXmax() * 2;
+  
+  
+  
+  TF1* funcBB = 0x0;
+  
+  Double_t parametersBBForward[nPar] = { 0, };
+  
+  TVirtualFitter::SetMaxIterations(5e6);
+  
+  if (fitType == AliTPCcalibResidualPID::kSaturatedLund) {
+    printf("Fit function: Saturated Lund\n");
+    
+    funcBB = new TF1("SaturatedLund", SaturatedLund, from, to, nPar);
+    //Double_t parametersBB[nPar] = {34.0446, 8.42221, 4.16724, 1.29473, 80.6663, 0}; //Xianguos values
+    //Double_t parametersBB[nPar] = {35.5, 8.7, 2.0, 1.09, 75.6, 0}; // No saturation
+    Double_t parametersBB[nPar] = {61.0, 8.7, 1.86, 0.85, 113.4, -38}; // Yields reasonable results for data and MC ~ all periods
+    
+    if (isPPb && !isMC) {
+      parametersBB[0] = 51.6;
+      parametersBB[1] = 9.7;
+      parametersBB[2] = 1.62;
+      parametersBB[3] = 0.99;
+      parametersBB[4] = 104.4;
+      parametersBB[5] = -27.0;
+    }
+    if (isMC) {
+      parametersBB[0] = 41.4;
+      parametersBB[1] = 8.6;
+      parametersBB[2] = 2.2;
+      parametersBB[3] = 0.92;
+      parametersBB[4] = 90.4;
+      parametersBB[5] = -20.0;
+    }
+    funcBB->SetParameters(parametersBB);
+    
+    Double_t parameterErrorsBB[nPar] = {5., 0.5, 0.2, 0.05, 10, 10};
+    funcBB->SetParErrors(parameterErrorsBB);
+    
+    for (Int_t i = 0; i < nPar; i++)
+      parametersBBForward[i] = parametersBB[i];
+  }
+  else if (fitType == AliTPCcalibResidualPID::kLund) {
+    printf("Fit function: Lund\n");
+    printf("****WARNING: Fit settings not tuned for this fit function, only for saturated Lund!\n");
+
+    funcBB = new TF1("SaturatedLund", SaturatedLund, from, to, nPar);
+    //Double_t parametersBB[nPar] = {34.0446, 8.42221, 4.16724, 1.29473, 80.6663, 0}; //Xianguos values
+    //Double_t parametersBB[nPar] = {35.5, 8.7, 2.0, 1.09, 75.6, 0}; // No saturation
+    Double_t parametersBB[nPar] = {35.0, 7., 1.86, 1., 75., 0}; // Yields reasonable results for data and MC ~ all periods
+    
+    funcBB->SetParameters(parametersBB);
+    
+    Double_t parameterErrorsBB[nPar] = {5., 0.5, 0.2, 0.05, 10, 10};
+    funcBB->SetParErrors(parameterErrorsBB);
+    
+    funcBB->FixParameter(5, 0.0); // No saturation
+    
+    for (Int_t i = 0; i < nPar; i++)
+      parametersBBForward[i] = parametersBB[i];
+  }
+  else if (fitType == kAlephWithAdditionalParam) {
+    printf("Fit function: Aleph with additional parameter\n");
+    printf("****WARNING: Fit settings not tuned for this fit function, only for saturated Lund!\n");
+    
+    // NOTE: This form is equivalent to the original form, but with parameter [2] redefined for better numerical stability.
+    // The additional parameter [5] has been introduced later and is unity originally. It seems not to be needed and is, thus,
+    // fixed to unity
+    funcBB = new TF1("funcAleph",
+                     "[0]/TMath::Power(TMath::Sqrt(1. + x*x)/x , [3])*([1]-[2]-[5]*TMath::Power(TMath::Sqrt(1. + x*x)/x , [3])-TMath::Log(1. + TMath::Power(x, -[4])*TMath::Exp(-[2])))", from, to);
+                     //OLD"[0]*([1]*TMath::Power(TMath::Sqrt(1 + x*x)/x , [3]) - [5] - TMath::Power(TMath::Sqrt(1 + x*x)/x , [3])*TMath::Log(TMath::Exp([2]) + 1/TMath::Power(x, [4])))", from, to); 
+    
+    Double_t parametersBB[nPar] = {2.6,14.3,-15,2.2,2.7, 0.06};
+    //OLD with different sign for [3]: Double_t parametersBB[nPar] = {0.0762*50.3,10.632,TMath::Log(1.34e-05),1.863,1.948, 1};
+    funcBB->SetParameters(parametersBB);
+    
+    for (Int_t i = 0; i < nPar; i++)
+      parametersBBForward[i] = parametersBB[i];
+  }
+  else if (fitType == AliTPCcalibResidualPID::kAleph) {
+    printf("Fit function: Aleph\n");
+    printf("****WARNING: Fit settings not tuned for this fit function, only for saturated Lund!\n");
+    
+    // NOTE: This form is equivalent to the original form, but with parameter [2] redefined for better numerical stability.
+    // The additional parameter [5] has been introduced later and is unity originally. It seems not to be needed and is, thus,
+    // fixed to unity
+    funcBB = new TF1("funcAleph",
+                     "[0]/TMath::Power(x/TMath::Sqrt(1. + x*x) , [3])*([1]-[2]-[5]*TMath::Power(x/TMath::Sqrt(1. + x*x) , [3])-TMath::Log(1. + TMath::Power(x, -[4])*TMath::Exp(-[2])))", from, to);
+                     //OLD"[0]*([1]*TMath::Power(TMath::Sqrt(1 + x*x)/x , [3]) - [5] - TMath::Power(TMath::Sqrt(1 + x*x)/x , [3])*TMath::Log(TMath::Exp([2]) + 1/TMath::Power(x, [4])))", from, to); 
+    //TEST Double_t parametersBB[nPar] = {1.2, 26.0, -30.0, -2.15, 5.6, 1};
+    Double_t parametersBB[nPar] = {1.25, 27.5, -29.0, 2.2, 5.2, 1};
+    
+    // For [5] floating: Double_t parametersBB[nPar] = {2.42,15.2,-16,-2.24,2.8, 0.057};
+    //OLD with different sign for [3] and [5] not fix: Double_t parametersBB[nPar] = {2.6,14.3,-15,2.2,2.7, 0.06};
+    //OLD with different sign for [3]: Double_t parametersBB[nPar] = {0.0762*50.3,10.632,TMath::Log(1.34e-05),1.863,1.948, 1};
+    funcBB->SetParameters(parametersBB);
+    
+    // Fix parameter 5 to original value of unity
+    funcBB->FixParameter(5, 1); 
+    
+    for (Int_t i = 0; i < nPar; i++)
+      parametersBBForward[i] = parametersBB[i];
+  }
+  else {
+    printf("Error: fitType not supported!\n");
+    return 0x0;
+  }
+  
+  funcBB->SetLineColor(2);
+  funcBB->SetLineWidth(1);
+  
+  // Override initial parameters, if user provides some
+  if (initialParameters) {
+    printf("Setting user initial parameters!\n");
+    funcBB->SetParameters(initialParameters);
+  }
+  
+  //
+  //
+  //
+  TGraphErrors * graphDelta = new TGraphErrors(*graphAll);
+  graphDelta->SetName("graphDelta");
+  
+  // In MC case: Remove errors from fit -> Some data points with extremely small errors otherwise completely dominate the fit,
+  // but these points could still be wrong due to systematics (low p effects, e.g.)
+  if (isMC) {
+    for(Int_t ip = 0; ip < graphAll->GetN(); ip++) {
+      graphAll->SetPointError(ip, 0, 0);
+    }
+  }
+  else if (useV0s) {
+    // Increase weight in plateau by reducing errors. Neccessary because errors are large there compared to other data points
+    for(Int_t ip = 0; ip < graphAll->GetN(); ip++) {
+      if (graphAll->GetX()[ip] >= 2500) { 
+        graphAll->SetPointError(ip, 0, graphAll->GetEY()[ip] / 6.0); 
+      }
+      // Same for pions in the very rel. rise
+      if (graphAll->GetX()[ip] >= 25 && graphAll->GetX()[ip] < 1000) {
+        graphAll->SetPointError(ip, 0, graphAll->GetEY()[ip] / 6.0); 
+      }
+      // Same for protons in the MIP region
+      if (graphAll->GetX()[ip] >= 2 && graphAll->GetX()[ip] < 4) {
+        graphAll->SetPointError(ip, 0, graphAll->GetEY()[ip] / 6.0); 
+      }
+    }
+  }
+  graphAll->Fit(funcBB, "REX0M");  
+  funcBB->SetRange(from, to);
+  funcBB->GetParameters(parametersBBForward);
+  //
+  //
+  //
+  for(Int_t ip = 0; ip < graphDelta->GetN(); ip++) {
+    graphDelta->GetY()[ip] -= funcBB->Eval(graphDelta->GetX()[ip]);
+    graphDelta->GetY()[ip] /= funcBB->Eval(graphDelta->GetX()[ip]);
+    graphDelta->GetEY()[ip] /= funcBB->Eval(graphDelta->GetX()[ip]);
+  }
+  TCanvas * canvDelta_1 = new TCanvas("canvDelta_1","control histogram for Bethe-Bloch fit 1",100,10,1380,800);
+  TCanvas * canvDelta_2 = new TCanvas("canvDelta_2","control histogram for Bethe-Bloch fit 2",100,10,1380,800);
+  canvDelta_1->SetGrid(1, 1);
+  canvDelta_1->SetLogx();
+  canvDelta_2->SetGrid(1, 1);
+  canvDelta_2->SetLogx();
+  /*
+  canvDelta->Divide(2, 1);
+  canvDelta->GetPad(1)->SetGrid(1, 1);
+  canvDelta->GetPad(1)->SetLogx();
+  canvDelta->GetPad(2)->SetGrid(1, 1);
+  canvDelta->GetPad(2)->SetLogx();
+  */
+
+  canvDelta_1->cd();
+  //canvDelta->cd(1);
+  TH1F *hBBdummy=new TH1F("hBBdummy","BB fit;#beta#gamma;#LTdE/dx#GT (arb. unit)",100,.8,1e4);
+  hBBdummy->SetMinimum(45);
+  hBBdummy->SetMaximum(120);
+  hBBdummy->GetXaxis()->SetTitleOffset(1.1);
+  hBBdummy->SetStats(kFALSE);
+  hBBdummy->Draw();
+
+  graphAll->SetTitle("BB multi-graph fit");
+  graphAll->SetMarkerStyle(22);
+  graphAll->SetMarkerColor(kMagenta);
+  graphAll->Draw("p");
+
+  TLegend *leg=new TLegend(.7,.12,.89,isMC?.4:.6);
+  leg->SetFillColor(10);
+  leg->SetBorderSize(1);
+
+  //overlay individual graphs
+  TGraph *gr=0x0;
+  if (isMC) {
+    gr=(TGraph*)inputGraphs->FindObject("Protons_MC_all");
+    gr->SetMarkerColor(kRed);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"p (MC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("Pions_MC_all");
+    gr->SetMarkerColor(kBlue);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"#pi (MC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("Kaons_MC_all");
+    gr->SetMarkerColor(kGreen);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"K (MC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("Electrons_MC_all");
+    gr->SetMarkerColor(kBlack);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"e (MC)","p");
+  }
+  else {
+    gr=(TGraph*)inputGraphs->FindObject("protonTpcGraph");
+    gr->SetMarkerColor(kRed);
+    gr->SetMarkerStyle(26);
+    gr->Draw("p");
+    leg->AddEntry(gr,"p (TPC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("protonTofGraph");
+    gr->SetMarkerColor(kRed);
+    gr->SetMarkerStyle(25);
+    gr->Draw("p");
+    leg->AddEntry(gr,"p (TOF)","p");
+    gr=(TGraph*)inputGraphs->FindObject("protonV0Graph");//ForBBfit");
+    gr->SetMarkerColor(kRed);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"p (V0)","p");
+    gr=(TGraph*)inputGraphs->FindObject("protonV0plusTOFGraph");//ForBBfit");
+    gr->SetMarkerColor(kRed);
+    gr->SetMarkerStyle(30);
+    gr->Draw("p");
+    leg->AddEntry(gr,"p (V0+TOF)","p");
+    gr=(TGraph*)inputGraphs->FindObject("pionTpcGraph");
+    gr->SetMarkerColor(kBlue);
+    gr->SetMarkerStyle(26);
+    gr->Draw("p");
+    leg->AddEntry(gr,"#pi (TPC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("pionTofGraph");
+    gr->SetMarkerColor(kBlue);
+    gr->SetMarkerStyle(25);
+    gr->Draw("p");
+    leg->AddEntry(gr,"#pi (TOF)","p");
+    gr=(TGraph*)inputGraphs->FindObject("pionV0Graph");//ForBBfit");
+    gr->SetMarkerColor(kBlue);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"#pi (V0)","p");
+    gr=(TGraph*)inputGraphs->FindObject("pionV0plusTOFGraph");//ForBBfit");
+    gr->SetMarkerColor(kBlue);
+    gr->SetMarkerStyle(30);
+    gr->Draw("p");
+    leg->AddEntry(gr,"#pi (V0+TOF)","p");
+    gr=(TGraph*)inputGraphs->FindObject("kaonTpcGraph");
+    gr->SetMarkerColor(kGreen);
+    gr->SetMarkerStyle(26);
+    gr->Draw("p");
+    leg->AddEntry(gr,"K (TPC)","p");
+    gr=(TGraph*)inputGraphs->FindObject("kaonTofGraph");
+    gr->SetMarkerColor(kGreen);
+    gr->SetMarkerStyle(25);
+    gr->Draw("p");
+    leg->AddEntry(gr,"K (TOF)","p");
+    gr=(TGraph*)inputGraphs->FindObject("electronGraph");
+    gr->SetMarkerColor(kBlack);
+    gr->SetMarkerStyle(25);
+    gr->Draw("p");
+    leg->AddEntry(gr,"e","p");
+    gr=(TGraph*)inputGraphs->FindObject("electronV0Graph");//ForBBfit");
+    gr->SetMarkerColor(kBlack);
+    gr->SetMarkerStyle(24);
+    gr->Draw("p");
+    leg->AddEntry(gr,"e (V0)","p");
+    gr=(TGraph*)inputGraphs->FindObject("electronV0plusTOFGraph");//ForBBfit");
+    gr->SetMarkerColor(kBlack);
+    gr->SetMarkerStyle(30);
+    gr->Draw("p");
+    leg->AddEntry(gr,"e (V0+TOF)","p");
+  }
+  
+  graphAll->GetXaxis()->SetTitle("#beta#gamma");
+  graphAll->GetYaxis()->SetTitle("<dE/dx> (arb. unit)");
+  graphAll->Draw("p");
+  funcBB->GetHistogram()->DrawClone("csame");
+  leg->AddEntry(graphAll,"used","p");
+  leg->AddEntry(funcBB,"fit","l");
+  leg->Draw("same");
+
+  canvDelta_2->cd();
+  //canvDelta->cd(2);
+  TH1F *hBBresdummy=new TH1F("hBBresdummy","residuals of BB fit;#beta#gamma;(data-fit)/fit",100,.8,1e4);
+  hBBresdummy->SetMinimum(-0.04);
+  hBBresdummy->SetMaximum(0.04);
+  hBBresdummy->GetXaxis()->SetTitleOffset(1.1);
+  hBBresdummy->GetYaxis()->SetTitleOffset(0.8);
+  hBBresdummy->SetStats(kFALSE);
+  hBBresdummy->Draw();
+
+  graphDelta->SetTitle("residuals of BB fit to multi-graph");
+  graphDelta->GetXaxis()->SetTitle("#beta#gamma");
+  graphDelta->GetYaxis()->SetTitle("(data - fit) / fit");
+  graphDelta->SetMarkerStyle(22);
+  graphDelta->SetMarkerColor(4);
+  graphDelta->Draw("p");
+  
+  TFile* fSave = TFile::Open("splines_QA_BetheBlochFit.root", "RECREATE");
+  fSave->cd();
+  canvDelta_1->Write();
+  canvDelta_2->Write();
+  
+  TString fitResults = "Fit results:\n";
+  for (Int_t i = 0; i < nPar; i++) {
+    fitResults.Append(Form("par%d:\t%f +- %f\n", i, funcBB->GetParameters()[i], funcBB->GetParErrors()[i]));
+  }
+  
+  TNamed* settings = new TNamed(fitResults.Data(), fitResults.Data());
+  settings->Write();
+  fSave->Close();
+  
+  
+  printf("\n\n%s", fitResults.Data());
+  
+  return funcBB;
+}
+
+
+//________________________________________________________________________
+Double_t AliTPCcalibResidualPID::Lund(Double_t* xx, Double_t* par)
+{
+  // bg is beta*gamma
+  const Double_t bg = xx[0];
+
+  const Double_t beta2 = bg*bg / (1.0 + bg*bg);
+  
+  const Double_t a = par[0];
+  const Double_t b = par[1];
+  const Double_t c = par[2];
+  const Double_t e = par[3];
+  const Double_t f = par[4];
+  
+  const Double_t d = TMath::Exp(c*(a - f) / b);
+  const Double_t powbg = TMath::Power(1.0 + bg, c);
+  const Double_t value = a / TMath::Power(beta2,e) +
+    b/c * TMath::Log(powbg / (1.0 + d*powbg));
+    
+  return value;
+}
+
+
+//________________________________________________________________________
+Double_t AliTPCcalibResidualPID::SaturatedLund(Double_t* xx, Double_t* par)
+{
+  const Double_t qq = Lund(xx, par);
+  return qq * TMath::Exp(par[5] / qq);
+}
+
+
+//________________________________________________________________________
+void AliTPCcalibResidualPID::FitSlicesY(TH2 *hist, Double_t heightFractionForRange, Int_t cutThreshold, TString fitOption, TObjArray *arr)
+{
+  //heightPercentageForRange
+  // custom slices fit
+  //
+
+  if (!hist) return;
+  if (!arr) return;
+
+  // If old style is to be used
+  /*
+  hist->FitSlicesY(0, 0, -1, cutThreshold, fitOption.Data(), &arr);
+  return;
+  */
+  
+  
+  arr->Clear();
+  arr->SetOwner();
+  arr->Expand(4);
+  
+  TAxis *axis=hist->GetXaxis();
+  const TArrayD *bins = axis->GetXbins();
+  TH1D** hList = new TH1D*[4];
+  
+  for (Int_t i = 0; i < 4; i++) {
+    delete gDirectory->FindObject(Form("%s_%d", hist->GetName(), i));
+    
+    if (bins->fN == 0) {
+      hList[i] = new TH1D(Form("%s_%d", hist->GetName(), i), i < 3 ? Form("Fitted value of par[%d]", i) : "Chi2/NDF",
+                          hist->GetNbinsX(), axis->GetXmin(), axis->GetXmax());
+    } else {
+      hList[i] = new TH1D(Form("%s_%d", hist->GetName(), i), i < 3 ? Form("Fitted value of par[%d]", i) : "Chi2/NDF", hist->GetNbinsX(), bins->fArray);
+    }
+    
+    (*arr)[i] = hList[i];
+  }
+  
+  for (Int_t ibin=axis->GetFirst(); ibin<=axis->GetLast(); ++ibin){
+    TH1 *h=hist->ProjectionY("_temp",ibin,ibin);
+    if (!h)
+      continue;
+    
+    if (h->GetEntries() < cutThreshold) {
+      delete h;
+      continue;
+    }
+    
+    // Average around maximum bin -> Might catch some outliers
+    Int_t maxBin = h->GetMaximumBin();
+    Double_t maxVal = h->GetBinContent(maxBin);
+    
+    if (maxVal < 2) { // It could happen that all entries are in overflow/underflow; don't fit in this case
+      delete h;
+      continue;
+    }
+    
+    UChar_t usedBins = 1;
+    if (maxBin > 1) {
+      maxVal += h->GetBinContent(maxBin - 1);
+      usedBins++;
+    }
+    if (maxBin < h->GetNbinsX()) {
+      maxVal += h->GetBinContent(maxBin + 1);
+      usedBins++;
+    }
+    maxVal /= usedBins;
+    
+    Double_t thresholdFraction = heightFractionForRange * maxVal; 
+    Int_t lowThrBin = TMath::Max(1, h->FindFirstBinAbove(thresholdFraction));
+    Int_t highThrBin = TMath::Min(h->GetNbinsX(), h->FindLastBinAbove(thresholdFraction));
+    
+    Double_t lowThreshold = h->GetBinCenter(lowThrBin);
+    Double_t highThreshold = h->GetBinCenter(highThrBin);
+
+    TFitResultPtr res = h->Fit("gaus", Form("%sS", fitOption.Data()), "", lowThreshold, highThreshold);
+    
+    if ((Int_t)res == 0) {
+      Int_t resBin = ibin;
+      hList[0]->SetBinContent(resBin,res->GetParams()[0]);
+      hList[0]->SetBinError(resBin,res->GetErrors()[0]);
+      hList[1]->SetBinContent(resBin,res->GetParams()[1]);
+      hList[1]->SetBinError(resBin,res->GetErrors()[1]);
+      hList[2]->SetBinContent(resBin,res->GetParams()[2]);
+      hList[2]->SetBinError(resBin,res->GetErrors()[2]);
+      hList[3]->SetBinContent(resBin,res->Ndf()>0?res->Chi2()/res->Ndf():0);
+    }
+    
+    delete h;
+  }
+}
+
+
+//______________________________________________________________________________
+Bool_t AliTPCcalibResidualPID::GetVertexIsOk(AliVEvent* event) const
+{
+  AliAODEvent* aod = 0x0;
+  AliESDEvent* esd = 0x0;
+  
+  aod = dynamic_cast<AliAODEvent*>(event);
+  if (!aod) {
+    esd = dynamic_cast<AliESDEvent*>(event);
+    
+    if (!esd) {
+      AliError("Event seems to be neither AOD nor ESD!");
+      return kFALSE;
+    }
+  }
+    
+  if (fIsPbpOrpPb) {
+    const AliVVertex* trkVtx = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertex()) :
+                                      dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertex()));
+      
+    if (!trkVtx || trkVtx->GetNContributors() <= 0)
+      return kFALSE;
+      
+    TString vtxTtl = trkVtx->GetTitle();
+    if (!vtxTtl.Contains("VertexerTracks"))
+      return kFALSE;
+      
+    Float_t zvtx = trkVtx->GetZ();
+    const AliVVertex* spdVtx = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertexSPD()) :
+                                      dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertexSPD()));
+    if (spdVtx->GetNContributors() <= 0)
+      return kFALSE;
+      
+    TString vtxTyp = spdVtx->GetTitle();
+    Double_t cov[6] = {0};
+    spdVtx->GetCovarianceMatrix(cov);
+    Double_t zRes = TMath::Sqrt(cov[5]);
+    if (vtxTyp.Contains("vertexer:Z") && (zRes > 0.25))
+      return kFALSE;
+      
+    if (TMath::Abs(spdVtx->GetZ() - trkVtx->GetZ()) > 0.5)
+      return kFALSE;
+
+    if (TMath::Abs(zvtx) > fZvtxCutEvent) //Default: 10 cm
+      return kFALSE;
+      
+    return kTRUE;
+  }
+    
+  
+  // pp and PbPb
+  const AliVVertex* primaryVertex = (aod ? dynamic_cast<const AliVVertex*>(aod->GetPrimaryVertex()) :
+                                           dynamic_cast<const AliVVertex*>(esd->GetPrimaryVertexTracks()));
+    
+  if (!primaryVertex || primaryVertex->GetNContributors() <= 0)
+    return kFALSE;
+      
+  if (TMath::Abs(primaryVertex->GetZ()) >= fZvtxCutEvent) //Default: 10 cm
+    return kFALSE;
+  
+  return kTRUE;
+}
+
+
+//______________________________________________________________________________
+void AliTPCcalibResidualPID::FillV0PIDlist(AliESDEvent* event)
+{
+  //
+  // Fill the PID tag list
+  //
+  
+  // If no event forwarded as parameter (default), cast current input event.
+  // Dynamic cast to ESD events (DO NOTHING for AOD events)
+  if (!event)
+    event = dynamic_cast<AliESDEvent *>(InputEvent());
+  
+  // If this fails, do nothing
+  if (!event)
+    return;
+  
+  if (!fV0KineCuts) {
+    AliError("V0KineCuts not available!");
+    return;
+  }
+  
+  TString beamType(event->GetBeamType());
+  
+  if (beamType.CompareTo("Pb-Pb") == 0 || beamType.CompareTo("A-A") == 0) {
+    fV0KineCuts->SetMode(AliESDv0KineCuts::kPurity, AliESDv0KineCuts::kPbPb);
+  }
+  else {
+    fV0KineCuts->SetMode(AliESDv0KineCuts::kPurity, AliESDv0KineCuts::kPP); 
+  }
+
+  // V0 selection
+  // set event
+  fV0KineCuts->SetEvent(event);
+
+  const Int_t numTracks = event->GetNumberOfTracks();
+  fV0tags = new Char_t[numTracks];
+  for (Int_t i = 0; i < numTracks; i++)
+    fV0tags[i] = 0;
+  
+  fV0motherIndex = new Int_t[numTracks];
+  for (Int_t i = 0; i < numTracks; i++)
+    fV0motherIndex[i] = -1;
+  
+  fV0motherPDG = new Int_t[numTracks];
+  for (Int_t i = 0; i < numTracks; i++)
+    fV0motherPDG[i] = 0;
+  
+  fNumTagsStored = numTracks;
+  
+  
+  
+  
+  
+  // loop over V0 particles
+  for (Int_t iv0 = 0; iv0 < event->GetNumberOfV0s(); iv0++) {
+    AliESDv0* v0 = (AliESDv0*)event->GetV0(iv0);
+    if (!v0)
+      continue;
+    
+    // Reject onFly V0's <-> Only take V0's from offline V0 finder
+    if (v0->GetOnFlyStatus())
+      continue; 
+  
+    // Get the particle selection 
+    Bool_t foundV0 = kFALSE;
+    Int_t pdgV0 = 0, pdgP = 0, pdgN = 0;
+    
+    foundV0 = fV0KineCuts->ProcessV0(v0, pdgV0, pdgP, pdgN);
+    if (!foundV0)
+      continue;
+    
+    Int_t iTrackP = v0->GetPindex();  // positive track
+    Int_t iTrackN = v0->GetNindex();  // negative track
+
+    /*
+    AliESDtrack* trackP = event->GetTrack(iTrackP);
+    AliESDtrack* trackN = event->GetTrack(iTrackN);
+    
+    if (!trackP || !trackN)
+      continue;
+    
+    
+    
+    Float_t xy = 999, z = 999;
+    trackP->GetImpactParameters(xy, z);
+    
+    Bool_t reject = kFALSE;
+    if (TMath::Abs(z) > 1)
+      continue;
+    
+    trackN->GetImpactParameters(xy, z);
+    if (TMath::Abs(z) > 1)
+      continue;
+    */
+    
+    
+    // Fill the Object arrays
+    // positive particles
+    if (pdgP == -11) {
+      fV0tags[iTrackP] = 14;
+    }
+    else if (pdgP == 211) {
+      fV0tags[iTrackP] = 15;
+    }
+    else if(pdgP == 2212) {
+      fV0tags[iTrackP] = 16;
+    }
+    
+    fV0motherIndex[iTrackP] = iv0;
+    fV0motherPDG[iTrackP] = pdgV0;
+
+    // negative particles
+    if( pdgN == 11){
+      fV0tags[iTrackN] = -14;
+    }
+    else if( pdgN == -211){
+      fV0tags[iTrackN] = -15;
+    }
+    else if( pdgN == -2212){
+      fV0tags[iTrackN] = -16;
+    }
+    
+    fV0motherIndex[iTrackN] = iv0;
+    fV0motherPDG[iTrackN] = pdgV0;
+
+  }
+}
+
+
+//______________________________________________________________________________
+void AliTPCcalibResidualPID::ClearV0PIDlist()
+{
+  //
+  // Clear the PID tag list
+  //
+
+  delete fV0tags;
+  fV0tags = 0;
+  
+  delete fV0motherIndex;
+  fV0motherIndex = 0;
+  
+  delete fV0motherPDG;
+  fV0motherPDG = 0;
+  
+  fNumTagsStored = 0;
+}
+
+
+//______________________________________________________________________________
+Char_t AliTPCcalibResidualPID::GetV0tag(Int_t trackIndex) const
+{
+  //
+  // Get the tag for the corresponding trackIndex. Returns -99 in case of invalid index/tag list.
+  //
+  
+  if (trackIndex < 0 || trackIndex >= fNumTagsStored || !fV0tags)
+    return -99;
+  
+  return fV0tags[trackIndex];
+}
+
+
+//______________________________________________________________________________
+Int_t AliTPCcalibResidualPID::GetV0motherIndex(Int_t trackIndex) const
+{
+  //
+  // Get the index of the V0 mother for the corresponding trackIndex. Returns -99 in case of invalid index/mother index list.
+  //
+  
+  if (trackIndex < 0 || trackIndex >= fNumTagsStored || !fV0motherIndex)
+    return -99;
+  
+  return fV0motherIndex[trackIndex];
+}
+
+
+//______________________________________________________________________________
+Int_t AliTPCcalibResidualPID::GetV0motherPDG(Int_t trackIndex) const
+{
+  //
+  // Get the PDG code of the V0 mother for the corresponding trackIndex. Returns 0 in case of invalid index/mother index list.
+  //
+  
+  if (trackIndex < 0 || trackIndex >= fNumTagsStored || !fV0motherPDG)
+    return 0;
+  
+  return fV0motherPDG[trackIndex];
+}
+
+
+//______________________________________________________________________________
+Int_t AliTPCcalibResidualPID::MergeGraphErrors(TGraphErrors* mergedGraph, TCollection* li) 
+{
+  // Adds all graphs with errors from the collection to this graph.
+  // Returns the total number of poins in the result or -1 in case of an error.
+  
+  // TODO "Normal" merge of latest root trunk will also take into account the error bars,
+  // so this function can be replaced by the normal root function with the latest root version.
+  
+  TIter next(li);
+  while (TObject* o = next()) {
+    TGraph *g = dynamic_cast<TGraph*>(o);
+    if (!g) {
+      Printf("ERROR: Cannot merge an object which doesn't inherit from TGraph found in the list");
+      return -1;
+    }
+    int n0 = mergedGraph->GetN();
+    int n1 = n0+g->GetN();
+    mergedGraph->Set(n1);
+    Double_t * x = g->GetX();
+    Double_t * y = g->GetY();
+    Double_t * ex = g->GetEX();
+    Double_t * ey = g->GetEY();
+    for (Int_t i = 0 ; i < g->GetN(); i++) {
+      mergedGraph->SetPoint(n0+i, x[i], y[i]);
+      Double_t exPoint = ex ? ex[i] : 0;
+      Double_t eyPoint = ey ? ey[i] : 0;
+      mergedGraph->SetPointError(n0+i, exPoint, eyPoint);
+    }
+  }
+  return mergedGraph->GetN();
+}
+
+//________________________________________________________________________
+Bool_t AliTPCcalibResidualPID::TPCCutMIGeo(const AliVTrack* track, const AliVEvent* evt, TTreeStream* streamer)
+{
+  //
+  // TPC Cut MIGeo
+  //
+
+  if (!track || !evt)
+    return kFALSE;
+  
+  const Short_t sign = track->Charge();
+  Double_t xyz[50];
+  Double_t pxpypz[50];
+  Double_t cv[100];
+
+  track->GetXYZ(xyz);
+  track->GetPxPyPz(pxpypz);
+
+  AliExternalTrackParam* par = new AliExternalTrackParam(xyz, pxpypz, cv, sign);
+  const AliESDtrack dummy;
+
+  const Double_t magField = evt->GetMagneticField();
+  Double_t varGeom = dummy.GetLengthInActiveZone(par, 3, 236, magField, 0, 0);
+  Double_t varNcr  = track->GetTPCClusterInfo(3, 1);
+  Double_t varNcls = track->GetTPCsignalN();
+
+  const Double_t varEval = 130. - 5. * TMath::Abs(1. / track->Pt());
+  Bool_t cutGeom   = varGeom > fgCutGeo * varEval;
+  Bool_t cutNcr    = varNcr  > fgCutNcr * varEval;
+  Bool_t cutNcls   = varNcls > fgCutNcl * varEval;
+
+  Bool_t kout = cutGeom && cutNcr && cutNcls;
+
+  if (streamer) {
+    Double_t dedx = track->GetTPCsignal();
+
+    (*streamer)<<"tree"<<
+      "param.="<< par<<
+      "varGeom="<<varGeom<<
+      "varNcr="<<varNcr<<
+      "varNcls="<<varNcls<<
+      
+      "cutGeom="<<cutGeom<<
+      "cutNcr="<<cutNcr<<
+      "cutNcls="<<cutNcls<<
+      
+      "kout="<<kout<<
+      "dedx="<<dedx<<
+      
+      "\n";
+  }
+  
+  delete par;
+  
+  return kout;
+}
diff --git a/PWGPP/TPC/AliTPCcalibResidualPID.h b/PWGPP/TPC/AliTPCcalibResidualPID.h
new file mode 100644 (file)
index 0000000..02b2eb7
--- /dev/null
@@ -0,0 +1,215 @@
+/**************************************************************************
+* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
+*                                                                        *
+* Author: Yvonne Pachmayer <pachmay@physi.uni-heidelberg.de>             *
+* 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.                  *
+**************************************************************************/
+//
+// The task:
+// stores TPC PID quantities in a THnSparse
+//
+//  Author:
+//  Yvonne Pachmayer <pachmay@physi.uni-heidelberg.de>
+//
+
+#ifndef ALITPCCALIBRESIDUALPID_H
+#define ALITPCCALIBRESIDUALPID_H
+#include "AliAnalysisTaskSE.h"
+
+#include <TTreeStream.h>
+#include "AliInputEventHandler.h"
+
+class TArrayF;
+template <class X>
+class THnSparseT;
+typedef class THnSparseT<TArrayF> THnSparseF;
+class TFile;
+class TGraphErrors;
+class AliESDEvent;
+class AliMCEvent;
+class AliESDtrackCuts;
+class AliESDpid;
+class AliESD;
+class AliAnalysisTask;
+class AliESDInputHandler;
+class AliESDv0KineCuts;
+class AliAnalysisManager;
+class AliCentrality;
+class TTree;
+class TSystem;
+class TStyle;
+class TROOT;
+class Riostream;
+class TChain;
+class TH2;
+class TF1;
+class TH1;
+class TObjArray;
+
+
+class AliTPCcalibResidualPID : public AliAnalysisTaskSE {
+ public:
+  enum FitType { kAleph = 0, kLund = 1, kSaturatedLund = 2, kAlephWithAdditionalParam = 3 };
+  AliTPCcalibResidualPID();
+  AliTPCcalibResidualPID(const char *name);
+  virtual ~AliTPCcalibResidualPID();
+  virtual void   UserCreateOutputObjects();
+  virtual void   UserExec(Option_t *);
+  virtual void   Process(AliESDEvent *const esdEvent=0, AliMCEvent *const mcEvent=0);
+  virtual void   Terminate(const Option_t*);
+  Int_t          CompareFloat(Float_t f1=1, Float_t f2=0) const;
+  //setter
+  virtual void   SetESDtrackCuts(AliESDtrackCuts * trackCuts){fESDtrackCuts = trackCuts;};
+  virtual void   SetESDtrackCutsV0(AliESDtrackCuts * trackCutsV0){fESDtrackCutsV0 = trackCutsV0;};
+  virtual void   SetProduceTPCsignalTHnSparse(Int_t producetpcsignal){fProduceTPCSignalSparse = producetpcsignal;};
+  virtual void   SetProducePIDqa(Int_t produceGlobal){fProduceGlobal = produceGlobal;};
+  virtual void   SetProduceAllPadsPID(Int_t produceAllpadTypes){fProduceAllPadTypes = produceAllpadTypes;};
+  virtual void   SetProduceShortPadsPID(Int_t produceShortpads){fProduceShortPads = produceShortpads;};
+  virtual void   SetProduceMediumPadsPID(Int_t produceMediumpads){fProduceMediumPads = produceMediumpads;};
+  virtual void   SetProduceLongPadsPID(Int_t produceLongpads){fProduceLongPads = produceLongpads;};
+  virtual void   SetProduceOrocPID(Int_t produceOroc){fProduceOroc = produceOroc;};
+  
+  virtual Bool_t GetVertexIsOk(AliVEvent* event) const;
+  
+  virtual Bool_t GetUseTPCCutMIGeo() const { return fUseTPCCutMIGeo; };
+  virtual void SetUseTPCCutMIGeo(Bool_t newValue) { fUseTPCCutMIGeo = newValue; };
+  
+  virtual Bool_t GetIsPbpOrpPb() const { return fIsPbpOrpPb; };
+  virtual void SetIsPbpOrpPb(Bool_t newValue) { fIsPbpOrpPb = newValue; };
+  
+  Double_t GetZvtxCutEvent() const { return fZvtxCutEvent; };
+  virtual void SetZvtxCutEvent(Double_t newValue) { fZvtxCutEvent = newValue; };
+  
+  Bool_t GetCorrectdEdxEtaDependence() const { return fCorrectdEdxEtaDependence; };
+  virtual void SetCorrectdEdxEtaDependence(Bool_t flag) { fCorrectdEdxEtaDependence = flag; };
+  
+  Bool_t GetCorrectdEdxMultiplicityDependence() const { return fCorrectdEdxMultiplicityDependence; };
+  virtual void SetCorrectdEdxMultiplicityDependence(Bool_t flag) { fCorrectdEdxMultiplicityDependence = flag; };
+  
+  virtual Char_t GetV0tag(Int_t trackIndex) const;
+
+  Bool_t GetUseMCinfo() const { return fUseMCinfo; };
+  virtual void SetUseMCinfo(Bool_t flag) { fUseMCinfo = flag; };
+  
+  virtual Int_t GetV0motherIndex(Int_t trackIndex) const;
+  virtual Int_t GetV0motherPDG(Int_t trackIndex) const;
+  
+  //
+  // static functions for postprocessing
+  //
+  static Double_t* ExtractResidualPID(THnSparseF * histPidQA,
+                                      const Bool_t useV0s = kTRUE,
+                                      const Char_t * outFile = "out.root",
+                                      const Char_t * type    = "MC",
+                                      const Char_t * period  = "LHC10H8",
+                                      const Char_t * pass    = "PASS1",
+                                      const Char_t * system  = "PBPB",
+                                      const Double_t * initialParameters = 0x0,
+                                      const Char_t * dedxtype= "",
+                                      FitType = kSaturatedLund);
+  static  TObjArray * GetResidualGraphs(THnSparseF * histPidQA, const Char_t * system, const Bool_t useV0s);
+  static  TObjArray * GetResidualGraphsMC(THnSparseF * histPidQA, const Char_t * system);
+  static  TObjArray * GetSeparation(THnSparseF * histPidQA, Int_t kParticle1, Int_t kParticle2);
+  static  TObjArray * GetResponseFunctions(TF1* parametrisation, TObjArray* inputGraphs, const Char_t * type, const Char_t * period, const Char_t * pass, const Char_t * system, const Char_t * dedxtype);
+  static  TF1*        FitBB(TObjArray* inputGraphs, Bool_t isMC, Bool_t isPPb, const Bool_t useV0s,
+                            const Double_t * initialParameters = 0x0, FitType = kSaturatedLund);
+  static Int_t MergeGraphErrors(TGraphErrors* mergedGraph, TCollection* li);
+  
+  static Double_t GetCutGeo() { return fgCutGeo; };
+  static Double_t GetCutNcr() { return fgCutNcr; };
+  static Double_t GetCutNcl() { return fgCutNcl; };
+  
+  static void SetCutGeo(Double_t value) { fgCutGeo = value; };
+  static void SetCutNcr(Double_t value) { fgCutNcr = value; };
+  static void SetCutNcl(Double_t value) { fgCutNcl = value; };
+  
+  static Bool_t TPCCutMIGeo(const AliVTrack* track, const AliVEvent* evt, TTreeStream* streamer = 0x0);
+  static Bool_t TPCCutMIGeo(const AliVTrack* track, const AliInputEventHandler* evtHandler, TTreeStream* streamer = 0x0)
+    { if (!evtHandler) return kFALSE; return TPCCutMIGeo(track, evtHandler->GetEvent(), streamer); };
+
+  protected:
+  static Double_t fgCutGeo;  // Cut variable for TPCCutMIGeo concerning geometry
+  static Double_t fgCutNcr;  // Cut variable for TPCCutMIGeo concerning num crossed rows
+  static Double_t fgCutNcl;  // Cut variable for TPCCutMIGeo concerning num clusters
+  
+  private:
+  static Double_t Lund(Double_t* xx, Double_t* par);
+  static Double_t SaturatedLund(Double_t* xx, Double_t* par);
+  
+  void  BinLogAxis(const THnSparseF *h, Int_t axisNumber);
+  enum {kElectron=0, kPion, kKaon, kProton} kParticle ;
+
+  static void FitSlicesY(TH2 *hist, Double_t heightFractionForRange, Int_t cutThreshold, TString fitOption, TObjArray *arr);
+
+  void FillV0PIDlist(AliESDEvent* esdEvent = 0x0);
+  void ClearV0PIDlist();
+
+  //
+  //
+  AliESDEvent *fESD;                   //! ESD object
+  AliMCEvent  *fMC;                    //! MC object
+  TObjArray * fOutputContainer;        //! output data container
+  AliESDtrackCuts * fESDtrackCuts;     // basic cut variables for all non-V0 tracks
+  AliESDtrackCuts * fESDtrackCutsV0;   // basic cut variables for all V0 tracks
+  AliESDpid * fESDpid;                 //! PID handling
+  //
+  
+  Bool_t fUseTPCCutMIGeo;   // Use geometrical cut for TPC 
+  
+  Bool_t fUseMCinfo;         // Use MC info, if available
+  
+  Bool_t fIsPbpOrpPb;      // Pbp/pPb collision or something else?
+  Double_t fZvtxCutEvent;  // Vertex z cut for the event (cm)
+  
+  AliESDv0KineCuts *fV0KineCuts;       //! ESD V0 kine cuts
+  Int_t fNumTagsStored;     // Number of entries of fV0tags
+  Char_t* fV0tags;         //! Pointer to array with tags for identified particles from V0 decays
+  Int_t* fV0motherIndex;   //! Pointer to array with index of the mother V0
+  Int_t* fV0motherPDG;     //! Pointer to array with pdg of the mother V0
+
+  Bool_t fProduceAllPadTypes, fProduceGlobal, fProduceShortPads, fProduceMediumPads, fProduceLongPads,fProduceOroc;
+  THnSparseF * fHistPidQA;             //! histogram for the QA of the PID
+  THnSparseF * fHistPidQAshort;        //! histogram for the QA of the PID short pads
+  THnSparseF * fHistPidQAmedium;       //! histogram for the QA of the PID med pads
+  THnSparseF * fHistPidQAlong;         //! histogram for the QA of the PID long pads
+  THnSparseF * fHistPidQAoroc;         //! histogram for the QA of the PID full oroc
+  //
+  Bool_t fProduceTPCSignalSparse;      //for setter
+  Bool_t fCorrectdEdxEtaDependence;    // Correct eta dependence for fHistPidQA (NOTE: Not done for the pad-specific THnSparses)
+  Bool_t fCorrectdEdxMultiplicityDependence; // Correct multiplicity dependence for fHistPidQA (NOTE: Not done for the pad-specific THnSparses)
+  THnSparseF * fThnspTpc;              //! thnsparse containing the data
+  //
+  //
+  
+  // QA histos
+  TObjArray* fQAList;           //! Array with QA histos
+  TH1F* fhInvMassGamma;         //! Histogram with inv. mass of gamma
+  TH1F* fhInvMassK0s;           //! Histogram with inv. mass of K0s
+  TH1F* fhInvMassLambda;        //! Histogram with inv. mass of lambda
+  TH1F* fhInvMassAntiLambda;    //! Histogram with inv. mass of anti-lambda
+  
+  TH2F* fhArmenterosAll;        //! Histogram with armenteros plot for all V0s
+  TH2F* fhArmenterosGamma;      //! Histogram with armenteros plot for gamma
+  TH2F* fhArmenterosK0s;        //! Histogram with armenteros plot for K0s
+  TH2F* fhArmenterosLambda;     //! Histogram with armenteros plot for lambda
+  TH2F* fhArmenterosAntiLambda; //! Histogram with armenteros plot for anti-lambda
+  
+  // QA histos for shared clusters
+  THnSparseF* fHistSharedClusQAV0Pi;  //! Histogram with shared clusters QA for V0 pi
+  THnSparseF* fHistSharedClusQAV0Pr;  //! Histogram with shared clusters QA for V0 pr
+  THnSparseF* fHistSharedClusQAV0El;  //! Histogram with shared clusters QA for V0 el
+  
+  AliTPCcalibResidualPID(const AliTPCcalibResidualPID&); // not implemented
+  AliTPCcalibResidualPID& operator=(const AliTPCcalibResidualPID&); // not implemented
+  
+  ClassDef(AliTPCcalibResidualPID, 4); 
+};
+#endif
diff --git a/PWGPP/TPC/macros/AddTaskTPCPIDEtaQA.C b/PWGPP/TPC/macros/AddTaskTPCPIDEtaQA.C
new file mode 100644 (file)
index 0000000..2121314
--- /dev/null
@@ -0,0 +1,106 @@
+AliAnalysisTask *AddTaskTPCPIDEtaQA(Int_t tpcCutType = AliTPCPIDBase::kTPCCutMIGeo /*AliTPCPIDBase::kTPCnclCut*/,
+                                    Bool_t usePhiCut = kFALSE,
+                                    Double_t ptThresholdForPhiCut = 0.0){
+  //get the current analysis manager
+  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
+  if (!mgr) {
+    Error("AddTask_bhess_PIDetaAdv", "No analysis manager found.");
+    return 0;
+  }
+  
+  //========= Add task to the ANALYSIS manager =====
+  AliTPCPIDEtaQA *task = new AliTPCPIDEtaQA("TPCPIDEtaQA");
+  task->SelectCollisionCandidates(AliVEvent::kMB | AliVEvent::kINT7);
+  
+  //
+  // Add track filters
+  //
+  AliAnalysisFilter* trackFilter = new AliAnalysisFilter("trackFilter");
+  AliESDtrackCuts* esdTrackCutsL = 0x0;
+  
+  printf("\nSettings:\n");
+  TString listOfFiles = gSystem->Getenv("LIST");
+  if (listOfFiles.Contains("LHC11") || listOfFiles.Contains("LHC12") || listOfFiles.Contains("LHC13")) {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    printf("Using standard ITS-TPC track cuts 2011\n");
+  }
+  else if (listOfFiles.Contains("LHC10")) {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2010(kTRUE);
+    printf("Using standard ITS-TPC track cuts 2010\n");
+  }
+  else  {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    printf("WARNING: Cuts not configured for this period!!! Using standard ITS-TPC track cuts 2011\n");
+  }
+  
+/*
+  esdTrackCutsL->SetMinNCrossedRowsTPC(120);
+  esdTrackCutsL->SetMinRatioCrossedRowsOverFindableClustersTPC(0.8);
+  esdTrackCutsL->SetMaxChi2PerClusterITS(36);
+  esdTrackCutsL->SetMaxFractionSharedTPCClusters(0.4);
+  esdTrackCutsL->SetMaxChi2TPCConstrainedGlobal(36);
+*/  
+
+  // Test whether we have pPb or Pbp
+  if (listOfFiles.Contains("pPb") || listOfFiles.Contains("Pbp")) {
+    task->SetIsPbpOrpPb(kTRUE);
+    printf("pPb/Pbp detected -> Adapting vertex cuts!\n");
+  }
+  else  {
+    task->SetIsPbpOrpPb(kFALSE);
+    printf("Collision type different from pPb/Pbp detected -> Using standard vertex cuts!\n");
+  }
+  
+  trackFilter->AddCuts(esdTrackCutsL);
+  task->SetTrackFilter(trackFilter);
+  
+  task->SetEtaCut(0.9);
+  task->SetUsePhiCut(usePhiCut);
+  task->SetPtThresholdForPhiCut(ptThresholdForPhiCut);
+  task->SetTPCcutType(tpcCutType);
+  
+  printf("Eta cut: %f\n", task->GetEtaCut());
+  printf("UsePhiCut: %d\n", task->GetUsePhiCut());
+  if (task->GetUsePhiCut())
+    printf("PtThresholdForPhiCut: %f\n", task->GetPtThresholdForPhiCut());
+  printf("UseTPCCutMIGeo: %d\n", task->GetUseTPCCutMIGeo());
+  printf("UseTPCnclCut: %d\n", task->GetUseTPCnclCut());
+  
+  
+  
+  
+  
+  
+  task->SetZvtxCutEvent(10.0);
+  printf("Cut on z position of vertex: %.2f cm\n", task->GetZvtxCutEvent());
+  
+  printf("UsePhiCut: %d\nPtThresholdForPhiCut: %.3f GeV/c\n\n", task->GetUsePhiCut(), task->GetPtThresholdForPhiCut());
+  mgr->AddTask(task);
+
+
+  //================================================
+  //              data containers
+  //================================================
+  //            find input container
+  //below the trunk version
+  AliAnalysisDataContainer *cinput  = mgr->GetCommonInputContainer();
+
+  //dumm output container
+  AliAnalysisDataContainer *coutput0 =
+      mgr->CreateContainer("TPCPIDEtaQA_tree",
+                           TTree::Class(),
+                           AliAnalysisManager::kExchangeContainer,
+                           "TPCPIDEtaQA_default");
+
+  //define output containers, please use 'username'_'somename'
+  AliAnalysisDataContainer *coutput1 = 
+      mgr->CreateContainer("TPCPIDEtaQA", TObjArray::Class(),
+                           AliAnalysisManager::kOutputContainer,"TPCPIDEtaQA.root");
+
+  //connect containers
+  mgr->ConnectInput  (task,  0, cinput );
+  mgr->ConnectOutput (task,  0, coutput0);
+  mgr->ConnectOutput (task,  1, coutput1);
+
+  return task;
+}
diff --git a/PWGPP/TPC/macros/AddTaskTPCPIDEtaTree.C b/PWGPP/TPC/macros/AddTaskTPCPIDEtaTree.C
new file mode 100644 (file)
index 0000000..e1d9e14
--- /dev/null
@@ -0,0 +1,131 @@
+AliAnalysisTask *AddTaskTPCPIDEtaTree(Bool_t correctdEdxEtaDependence = kFALSE, Bool_t correctdEdxMultiplicityDependence = kFALSE,
+                                      Bool_t setDoAdditionalQA = kFALSE, Bool_t useTPCCutMIGeo = kTRUE, Bool_t usePhiCut = kFALSE){
+  //get the current analysis manager
+  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
+  if (!mgr) {
+    Error("AddTask_bhess_PIDetaTree", "No analysis manager found.");
+    return 0;
+  }
+  
+  //========= Add task to the ANALYSIS manager =====
+  AliTPCPIDEtaTree *task = new AliTPCPIDEtaTree("TPCPIDEtaTree");
+  task->SelectCollisionCandidates(AliVEvent::kMB | AliVEvent::kINT7);
+  
+  //
+  // Add track filters
+  //
+  AliAnalysisFilter* trackFilter = new AliAnalysisFilter("trackFilter");
+  AliESDtrackCuts* esdTrackCutsL = 0x0;
+  
+  printf("\nSettings:\n");
+  TString listOfFiles = gSystem->Getenv("LIST");
+  if (listOfFiles.Contains("LHC11") || listOfFiles.Contains("LHC12") || listOfFiles.Contains("LHC13")) {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    printf("Using standard ITS-TPC track cuts 2011.\n");
+  }
+  else if (listOfFiles.Contains("LHC10")) {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2010(kTRUE);
+    printf("Using standard ITS-TPC track cuts 2010.\n");
+  }
+  else  {
+    esdTrackCutsL = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    printf("WARNING: Cuts not configured for this period!!! Using standard ITS-TPC track cuts 2011\n");
+  }
+  
+  if (listOfFiles.Contains("PbPb") || listOfFiles.Contains("pPb") || listOfFiles.Contains("Pbp")) {
+    task->SetStoreMultiplicity(kTRUE);
+    printf("PbPb, pPb or Pbp detected -> Storing multiplicity in tree!\n");
+  }
+  else  {
+    task->SetStoreMultiplicity(kFALSE);
+    printf("pp detected -> NOT storing multiplicity in tree!\n");
+  }
+  
+  
+  // Test whether we have pPb or Pbp
+  if (listOfFiles.Contains("pPb") || listOfFiles.Contains("Pbp")) {
+    task->SetIsPbpOrpPb(kTRUE);
+    printf("pPb/Pbp detected -> Adapting vertex cuts!\n");
+  }
+  else  {
+    task->SetIsPbpOrpPb(kFALSE);
+    printf("Collision type different from pPb/Pbp detected -> Using standard vertex cuts!\n");
+  }
+  
+  
+  trackFilter->AddCuts(esdTrackCutsL);
+  task->SetTrackFilter(trackFilter);
+  task->SetUsePhiCut(usePhiCut);
+  task->SetUseTPCCutMIGeo(useTPCCutMIGeo);
+  
+  printf("UsePhiCut: %d\n", task->GetUsePhiCut());
+  printf("UseTPCCutMIGeo: %d\n", task->GetUseTPCCutMIGeo());
+  
+  
+  task->SetDoAdditionalQA(setDoAdditionalQA);
+  
+  if (task->GetDoAdditionalQA())
+    printf("Storing histos for additional QA!\n");
+  else
+    printf("NOT storing histos for additional QA!\n");
+  
+  task->SetZvtxCutEvent(10.0);
+  printf("Cut on z position of vertex: %.2f cm\n", task->GetZvtxCutEvent());
+  
+  task->SetEtaCut(0.9);
+  printf("EtaCut: %.2f\n", task->GetEtaCut());
+  
+  task->SetPtpcPionCut(0.6);
+  printf("P_TPC_Pion cut: %.2f\n", task->GetPtpcPionCut());
+  
+  task->SetStoreNumOfSubthresholdclusters(kTRUE);
+  printf("Store num subthreshold clusters: %d\n", task->GetStoreNumOfSubthresholdclusters());
+  
+  task->SetStoreNumClustersInActiveVolume(kTRUE);
+  printf("Store num clusters in active volume: %d\n", task->GetStoreNumClustersInActiveVolume());
+  
+  task->SetCorrectdEdxEtaDependence(correctdEdxEtaDependence);  
+  task->SetCorrectdEdxMultiplicityDependence(correctdEdxMultiplicityDependence);
+  
+  printf("Eta correction: %s for this task\n", 
+         task->GetCorrectdEdxEtaDependence() ? "enabled (only works if enabled in PIDresponse!)" : "explicitly disabled");
+  printf("Multiplicity correction: %s for this task\n\n", 
+         task->GetCorrectdEdxMultiplicityDependence() ? "enabled (only works if enabled in PIDresponse!)" : "explicitly disabled");
+  
+  mgr->AddTask(task);
+
+
+  //================================================
+  //              data containers
+  //================================================
+  //            find input container
+  //below the trunk version
+  AliAnalysisDataContainer *cinput  = mgr->GetCommonInputContainer();
+
+  //dumm output container
+  AliAnalysisDataContainer *coutput0 =
+      mgr->CreateContainer("TPCPIDEtaTree",
+                           TTree::Class(),
+                           AliAnalysisManager::kExchangeContainer,
+                           "TPCPIDEtaTree_default");
+
+  //define output containers, please use 'username'_'somename'
+  AliAnalysisDataContainer *coutput1 = 
+      mgr->CreateContainer("TPCPIDEtaTree", TTree::Class(),
+                           AliAnalysisManager::kOutputContainer,"TPCPIDEtaTree.root");
+  AliAnalysisDataContainer *coutput2 = 
+      mgr->CreateContainer("TPCPIDEtaTree", TTree::Class(),
+                           AliAnalysisManager::kOutputContainer,"TPCPIDEtaTreePions.root");
+  AliAnalysisDataContainer *coutput3 = 
+      mgr->CreateContainer("TPCPIDEtaTreeAdditionalQA", TObjArray::Class(),
+                           AliAnalysisManager::kOutputContainer,"TPCPIDEtaTreeAddionalQA.root");
+
+  //connect containers
+  mgr->ConnectInput(task,  0, cinput );
+  mgr->ConnectOutput(task,  0, coutput0);
+  mgr->ConnectOutput(task,  1, coutput1);
+  mgr->ConnectOutput(task, 2, coutput2); 
+  mgr->ConnectOutput(task, 3, coutput3); 
+
+  return task;
+}
diff --git a/PWGPP/TPC/macros/AddTaskTPCcalibResidualPID.C b/PWGPP/TPC/macros/AddTaskTPCcalibResidualPID.C
new file mode 100644 (file)
index 0000000..ae806a8
--- /dev/null
@@ -0,0 +1,107 @@
+AliAnalysisTask *AddTaskTPCcalibResidualPID(Bool_t producePIDqa = kTRUE, Bool_t useTPCCutMIGeo = kTRUE,
+                                            Bool_t correctdEdxEtaDependence = kFALSE, 
+                                            Bool_t correctdEdxMultiplicityDependence = kFALSE,
+                                            Bool_t useMCinfo = kTRUE){
+  //get the current analysis manager
+  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
+  if (!mgr) {
+    Error("AddTask_statsQA_TPCresPID", "No analysis manager found.");
+    return 0;
+  }
+
+  TString trainConfig=gSystem->Getenv("CONFIG_FILE");
+
+  TString list=gSystem->Getenv("LIST");
+  Bool_t isLHC11h = list.Contains("LHC11h");
+
+
+  //========= Add task to the ANALYSIS manager =====
+  AliTPCcalibResidualPID *task=new AliTPCcalibResidualPID("TPCresPID");
+  task->SelectCollisionCandidates(AliVEvent::kMB | AliVEvent::kINT7);
+
+  // which THnSparse should be produced
+  task->SetProducePIDqa(producePIDqa);
+  
+  AliESDtrackCuts* esdTrackCuts = 0x0;
+  AliESDtrackCuts* esdTrackCutsV0 = 0x0;
+  
+  //TODO LHC12 + check if cuts are correct in this way!
+  TString listOfFiles = gSystem->Getenv("LIST");
+  if (listOfFiles.Contains("LHC11") || listOfFiles.Contains("LHC12") || listOfFiles.Contains("LHC13")) {
+    esdTrackCuts = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    esdTrackCutsV0 = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kFALSE);
+    task->SetESDtrackCuts(esdTrackCuts);
+    //task->SetESDtrackCutsV0(esdTrackCutsV0);
+    printf("Using standard ITS-TPC track cuts 2011.\n");
+  }
+  else if (listOfFiles.Contains("LHC10")) {
+    esdTrackCuts = AliESDtrackCuts::GetStandardITSTPCTrackCuts2010(kTRUE);
+    esdTrackCutsV0 = AliESDtrackCuts::GetStandardITSTPCTrackCuts2010(kFALSE);
+    task->SetESDtrackCuts(esdTrackCuts);
+    //task->SetESDtrackCutsV0(esdTrackCutsV0);
+    printf("Using standard ITS-TPC track cuts 2010.\n");
+  }
+  else  {
+    esdTrackCuts = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kTRUE);
+    esdTrackCutsV0 = AliESDtrackCuts::GetStandardITSTPCTrackCuts2011(kFALSE);
+    task->SetESDtrackCuts(esdTrackCuts);
+    //task->SetESDtrackCutsV0(esdTrackCutsV0);
+    
+    printf("WARNING: Cuts not configured for this period!!! Using standard ITS-TPC track cuts 2011\n");
+  }
+  
+  // Test whether we have pPb or Pbp
+  if (listOfFiles.Contains("pPb") || listOfFiles.Contains("Pbp")) {
+    task->SetIsPbpOrpPb(kTRUE);
+    printf("pPb/Pbp detected -> Adapting vertex cuts!\n");
+  }
+  else  {
+    task->SetIsPbpOrpPb(kFALSE);
+    printf("Collision type different from pPb/Pbp detected -> Using standard vertex cuts!\n");
+  }
+
+  task->SetZvtxCutEvent(10.0);
+  printf("Cut on z position of vertex: %.2f cm\n", task->GetZvtxCutEvent());
+  
+  task->SetUseTPCCutMIGeo(useTPCCutMIGeo);
+  printf("UseTPCCutMIGeo: %d\n", task->GetUseTPCCutMIGeo());
+  
+  task->SetCorrectdEdxEtaDependence(correctdEdxEtaDependence);
+  
+  task->SetCorrectdEdxMultiplicityDependence(correctdEdxMultiplicityDependence);
+  
+  task->SetUseMCinfo(useMCinfo);
+  
+  printf("Eta correction: %s for this task\n", 
+         task->GetCorrectdEdxEtaDependence() ? "enabled (only works if enabled in PIDresponse!)" : "explicitly disabled");
+  
+  printf("Multiplicity correction: %s for this task\n", 
+         task->GetCorrectdEdxMultiplicityDependence() ? "enabled (only works if enabled in PIDresponse!)" : "explicitly disabled");
+
+  printf("Use MC info: %d\n", task->GetUseMCinfo());
+  
+  
+  mgr->AddTask(task);
+
+  //================================================
+  //              data containers
+  //================================================
+  //            find input container
+  
+  
+  //            define output containers, please use 'username'_'somename'
+  AliAnalysisDataContainer *coutput1 =
+    mgr->CreateContainer("TPCresPID", TObjArray::Class(),
+                         AliAnalysisManager::kOutputContainer,"TPCresidualPID.root");
+    
+  AliAnalysisDataContainer *coutput2 =
+    mgr->CreateContainer("TPCresPIDQA", TObjArray::Class(),
+                         AliAnalysisManager::kOutputContainer,"TPCresidualPIDQA.root");
+  
+  //           connect containers
+  mgr->ConnectInput  (task,  0, mgr->GetCommonInputContainer() );
+  mgr->ConnectOutput (task,  1, coutput1);
+  mgr->ConnectOutput (task,  2, coutput2);
+  
+  return task;
+}
diff --git a/PWGPP/TPC/macros/PIDCalib/ProgressBar.h b/PWGPP/TPC/macros/PIDCalib/ProgressBar.h
new file mode 100644 (file)
index 0000000..6b559f1
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef PROGRESSBAR_H
+#define PROGRESSBAR_H
+
+static Bool_t printedSomething = kFALSE;
+static Int_t progressCounter = 0;
+static const TString slash[4] = { "\\", "-", "/", "|" };
+void progressbar(Int_t percent)
+{
+  // If something else has been printed, do not overwrite!
+  if (printedSomething) {
+    std::cout << std::endl;
+    printedSomething = kFALSE;
+  }
+  std::cout << "\r"; // carriage return back to beginning of line
+  for (Int_t i = 0; i < percent; i++)
+    std::cout << ".";
+    
+  std::cout << " " << slash[progressCounter].Data() << " " << percent << " %" << std::flush; // print the bars and percentage
+  progressCounter++; // increment to make the slash appear to rotate
+  if(progressCounter == 4)
+    progressCounter = 0; // reset slash animation
+}
+
+
+#endif
diff --git a/PWGPP/TPC/macros/PIDCalib/THnSparseDefinitions.h b/PWGPP/TPC/macros/PIDCalib/THnSparseDefinitions.h
new file mode 100644 (file)
index 0000000..972cd1c
--- /dev/null
@@ -0,0 +1,435 @@
+#ifndef THNSPARSEDEFINITIONS_H
+#define THNSPARSEDEFINITIONS_H
+
+#ifndef __CINT__
+#include "TCanvas.h"
+#include "THnSparse.h"
+#include "TPaveText.h"
+#include "TString.h"
+#include "TGraphAsymmErrors.h"
+#include "AliPID.h"
+#endif
+
+#define NEW_AXES
+
+#ifdef NEW_AXES
+  enum axesTHnSparseEta {
+    kMCpid = 0,
+    kSelectSpecies,
+    kPtpcInner,
+    kMultiplicity,
+    kDeltaPrime,
+    kEta
+  };
+#else
+  enum axesTHnSparseEta {
+    kMCpid = 0,
+    kSelectSpecies,
+    kPtpcInner,
+    kPt,
+    kDeDx,
+    kMultiplicity,//kDelta,
+    kDeltaPrime,
+    kEta
+  };
+#endif
+
+enum axesTHnSparsePID {
+  kPidMCpid = 0,
+  kPidSelectSpecies,
+  kPidPt,
+  //OLD kPidDelta,
+  kPidDeltaPrime,
+  kPidCentrality,
+  kPidJetPt,
+  kPidZ,
+  kPidXi
+};
+
+/*OLD with TOF, p_TPC_Inner and p_vertex
+enum axesTHnSparsePID {
+  kPidMCpid = 0,
+  kPidSelectSpecies,
+  kPidPtpcInner,
+  kPidPt,
+  kPidPvertex,
+  kPidDelta,
+  kPidDeltaPrime,
+  kPidDeltaTOF
+};//*/
+
+enum axesTHnSparsePIDgen {
+  kPidGenMCpid = 0,
+  kPidGenSelectSpecies,
+  kPidGenPt,
+  //OLD kPidGenDelta,
+  kPidGenDeltaPrime,
+  kPidGenCentrality,
+  kPidGenJetPt,
+  kPidGenZ,
+  kPidGenXi
+};
+
+enum axesTHnSparsePIDgenYield {
+  kPidGenYieldMCpid = 0,
+  kPidGenYieldPt = 1,
+  kPidGenYieldCentrality = 2,
+  kPidGenYieldJetPt = 3,
+  kPidGenYieldZ = 4,
+  kPidGenYieldXi = 5,
+  kPidGenYieldNumAxes = 6 
+};
+
+enum MCpid  {
+  kEl = 1,
+  kKa = 2,
+  kMu = 3,
+  kPi = 4,
+  kPr = 5,
+  kMuPlusPi = 10
+};
+
+enum PIDtype { 
+  kMCid = 0, 
+  kTPCid = 1, 
+  kV0idNoTOF = 2, 
+  kTPCandTOFid = 3,
+  kV0idPlusTOFaccepted = 4,
+  kV0idPlusTOFrejected = 5  
+};
+
+enum efficiencyAxes {
+  kEffMCID = 0, 
+  kEffTrackPt = 1, 
+  kEffTrackEta = 2, 
+  kEffTrackCharge = 3, 
+  kEffCentrality = 4, 
+  kEffJetPt = 5,
+  kEffNumAxes = 6 
+};
+
+enum ptResolutionAxes { 
+  kPtResJetPt = 0,
+  kPtResGenPt = 1,
+  kPtResRecPt = 2,
+  kPtResCharge = 3,
+  kPtResCentrality = 4, 
+  kPtResNumAxes = 5
+};
+
+enum dEdxCheckAxes {
+  kDeDxCheckPID = 0,
+  kDeDxCheckP = 1,
+  kDeDxCheckJetPt = 2,
+  kDeDxCheckEtaAbs = 3,
+  kDeDxCheckDeDx = 4,
+  kDeDxCheckNumAxes = 5 
+};
+  
+enum EffSteps {
+  kStepGenWithGenCuts = 0, 
+  kStepRecWithGenCuts = 1, 
+  kStepRecWithGenCutsMeasuredObs = 2,
+  kStepRecWithRecCutsMeasuredObs = 3, 
+  kStepRecWithRecCutsMeasuredObsPrimaries = 4,
+  kStepRecWithRecCutsMeasuredObsStrangenessScaled = 5,
+  kStepRecWithRecCutsPrimaries = 6,
+  kNumSteps = 7
+};
+
+enum chargeMode {
+  kNegCharge = -1,
+  kAllCharged = 0,
+  kPosCharge = 1
+};
+
+enum TOFpidInfo {
+  kNoTOFinfo = -2,
+  kNoTOFpid = -1,
+  kTOFpion = 0,
+  kTOFkaon = 1,
+  kTOFproton = 2,
+  kNumTOFspecies = 3,
+  kNumTOFpidInfoBins = 5
+};
+
+const TString partShortName[9] = { "El", "Ka", "Mu", "Pi", "Pr", "V0plusTOFel", "V0el", "V0pi", "V0pr" };
+
+///*
+//coarser binning at high pT and in general coarser binning to get reasonable weighting for regularisation
+const Int_t nPtBins = 53;
+const Double_t binsPt[nPtBins+1] = {0., 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45,
+             0.5, 0.55, 0.6, 0.65, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4,
+             1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4,
+             3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0,
+             9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0,
+             24.0, 30.0, 40.0, 50.0 };
+//*/
+             
+/*
+//coarser binning at high pT and, in addition, coarser binning around crossings for PbPb 2.76 ATeV FINAL version
+const Int_t nPtBins = 50;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.95, 1.1,
+             1.3, 1.4, 1.8, 2.2, 2.6, 3.0, 3.2, 3.4, 3.6, 3.8, 
+             4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 
+             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0, 
+             50.0 };
+*/
+
+
+
+/*
+//coarser binning at high pT and, in addition, coarser binning around crossings for pPb 5.023 ATeV FINAL version
+const Int_t nPtBins = 52;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,
+             1.0, 1.1, 1.2, 1.3, 1.4, 1.8, 2.2, 2.6, 3.0, 3.2, 
+             3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 
+             8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0,
+             22.0, 30.0, 50.0 };
+*/
+
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 7 TeV FINAL version
+const Int_t nPtBins = 47;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6,  0.65, 0.7, 0.75, 0.8, 1.2, 1.4, 1.6,
+             1.8, 2.0, 2.6, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5,
+             5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 
+             13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 50.0 };
+*/
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 7 TeV VERY NEW version
+const Int_t nPtBins = 46;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6,  0.65, 0.7, 0.75, 0.8, 1.2, 1.6,
+             2.0, 2.5, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0,
+             5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0,
+             14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0, 50.0 };
+*/
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 7 TeV NEW version
+const Int_t nPtBins = 47;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6,  0.65, 0.7, 0.75, 0.9, 1.2, 1.4, 1.6,
+             2.0, 2.5, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0,
+             5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0,
+             14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0, 50.0 };
+*/
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 7 TeV
+/*
+const Int_t nPtBins = 50;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5, 0.55, 0.6,  0.65, 0.7, 0.75, 0.9, 1.1, 1.2, 1.3,
+             1.4, 1.6, 1.8, 2.0, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8,
+             4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0,
+             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0,
+             50.0 };
+*/
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 2.76 TeV FINAL version
+const Int_t nPtBins = 45;
+const Double_t binsPt[nPtBins+1] = {0., 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45,
+             0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 1.05,  
+             1.2, 1.3, 1.4, 1.8, 2.9, 3.4, 3.6, 3.8, 4.0, 4.5, 
+             5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 
+             13.0, 14.0, 15.0, 16.0, 20.0, 50.0 };
+*/
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 2.76 TeV NEW version
+const Int_t nPtBins = 49;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5,  0.55, 0.6,  0.65, 0.7,  0.75, 0.8,  0.85, 0.9,
+             1.05,  1.2,  1.3 , 1.4,  1.7,  2.4, 3.2 , 3.4 , 3.6,  3.8 ,
+             4.0,  4.5 , 5.0,  5.5 , 6.0,  6.5 , 7.0,  8.0 , 9.0,  10.0,
+             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0,
+             50.0 };
+*/
+/*
+// Coarser binning at high pT and, in addition, coarser binning around crossings for pp 2.76 TeV
+const Int_t nPtBins = 50;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5,  0.55, 0.6,  0.65, 0.7,  0.75, 0.8,  0.85, 0.9,  0.95,
+             1.0,  1.1 , 1.2,  1.3 , 1.4, 1.8,  2.4, 3.2,  3.6,  3.8 ,
+             4.0,  4.5 , 5.0,  5.5 , 6.0,  6.5 , 7.0,  8.0 , 9.0,  10.0,
+             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0,
+             50.0 };
+*/
+
+/*
+//coarser binning at high pT
+const Int_t nPtBins = 60;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+             0.5,  0.55, 0.6,  0.65, 0.7,  0.75, 0.8,  0.85, 0.9,  0.95,
+             1.0,  1.1 , 1.2,  1.3 , 1.4,  1.5 , 1.6,  1.7 , 1.8,  1.9 ,
+             2.0,  2.2 , 2.4,  2.6 , 2.8,  3.0 , 3.2,  3.4 , 3.6,  3.8 ,
+             4.0,  4.5 , 5.0,  5.5 , 6.0,  6.5 , 7.0,  8.0 , 9.0,  10.0,
+             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 22.0, 26.0, 35.0,
+             50.0 };
+*/
+             
+/* OLD default as used in PID-Task to create THnSparses
+const Int_t nPtBins = 68;
+const Double_t binsPt[nPtBins+1] = {0. ,  0.05, 0.1,  0.15, 0.2,  0.25, 0.3,  0.35, 0.4,  0.45,
+                              0.5,  0.55, 0.6,  0.65, 0.7,  0.75, 0.8,  0.85, 0.9,  0.95,
+                              1.0,  1.1 , 1.2,  1.3 , 1.4,  1.5 , 1.6,  1.7 , 1.8,  1.9 ,
+                              2.0,  2.2 , 2.4,  2.6 , 2.8,  3.0 , 3.2,  3.4 , 3.6,  3.8 ,
+                              4.0,  4.5 , 5.0,  5.5 , 6.0,  6.5 , 7.0,  8.0 , 9.0,  10.0,
+                              11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0,
+                              26.0, 28.0, 30.0, 32.0, 34.0, 36.0, 40.0, 45.0, 50.0 };
+*/
+             
+const Int_t nDeDxBins = 19;
+const Double_t binsDeDx[nDeDxBins+1] = {50., 52., 54., 56., 58., 60., 65., 70., 75., 80., 
+                                        85., 90., 100., 120., 160., 200., 250., 300., 400., 600. };           
+/*                            
+const Int_t nDeDxBins = 35;
+const Double_t binsDeDx[nDeDxBins+1] = {50., 51., 52., 53., 54., 55., 56., 57., 58., 59.,
+             60., 62., 64., 66., 68., 70., 72., 74., 76., 78.,
+             80., 85., 90., 95., 100., 120., 140., 160., 180., 200.,
+             250., 300., 350., 400., 500, 650.};*/
+
+//____________________________________________________________________________________________________________________
+Int_t getLineColor(Int_t ID) {
+  switch (ID) {
+    case kEl:
+      // El
+      return kMagenta;
+    case kKa:
+      // Ka
+      return kGreen;
+    case kMu:
+      // Mu
+      return kOrange -3;
+    case kPi:
+      // Pi
+      return kRed;
+    case kPr:
+      // Pr
+      return kBlue;
+    case kMuPlusPi:
+      // Muons plus pions
+      return kCyan;
+    default:
+      return 0;
+  }
+  
+  return 0;
+}
+
+
+//____________________________________________________________________________________________________________________
+Int_t getLineColorAliPID(Int_t ID) {
+  switch (ID) {
+    case AliPID::kElectron:
+      // El
+      return kMagenta;
+    case AliPID::kKaon:
+      // Ka
+      return kGreen;
+    case AliPID::kMuon:
+      // Mu
+      return kOrange - 3;
+    case AliPID::kPion:
+      // Pi
+      return kRed;
+    case AliPID::kProton:
+      // Pr
+      return kBlue;
+    default:
+      return 0;
+  }
+  
+  return 0;
+}
+
+
+//____________________________________________________________________________________________________________________
+void ClearTitleFromHistoInCanvas(TCanvas* c, Int_t padNum = -1)
+{
+  // Remove the title from a histogram plotted in the canvase without 
+  // removing the title from the histogram itself.
+  // If padNum is >= 0, this method will be applied to the corresponding
+  // pad number
+  
+  c->Update();    // Update in order to have access to the title in the following
+
+  TPaveText* paveTextTitle = (padNum >= 0) ? (TPaveText*)c->GetPad(padNum)->FindObject("title") : (TPaveText*)c->FindObject("title");
+  if (paveTextTitle) 
+    paveTextTitle->Clear();
+}
+
+
+//____________________________________________________________________________________________________________________
+Int_t GetAxisByTitle(const THnSparse* h, TString title)
+{
+  if (!h)
+    return -1;
+  
+  for (Int_t iDim = 0; iDim < h->GetNdimensions(); iDim++) {
+    if (!title.CompareTo(h->GetAxis(iDim)->GetTitle()))
+      return iDim;
+  }
+
+  return -1;
+}
+
+
+//____________________________________________________________________________________________________________________
+TGraphAsymmErrors* HistToGraph(const TString grname, const TH1 *hh, const Double_t thres=0, const TH1 *herr=0x0, const Double_t xmin=-1e10, const Double_t xmax=1e10)
+{
+  if (!hh)
+    return 0x0;
+  
+  const Int_t nbin = hh->GetNbinsX();
+  Double_t xxs[nbin], yys[nbin], exs[nbin], eys[nbin];
+  Int_t np=0;
+  for(Int_t ii=1; ii<=nbin; ii++){
+    const Double_t iyy = hh->GetBinContent(ii);
+    if(iyy<=thres)
+      continue;
+
+    const Double_t iey = hh->GetBinError(ii);
+    if(iey<1e-15){
+      if(iyy>1e-15){
+        printf("HistToGraph warning! should be fine if this is ratio %d %e %e\n", ii, iyy, iey); //exit(1);
+      }
+      //continue;
+    }
+
+    const Double_t ixx = hh->GetBinCenter(ii);
+    if(ixx<xmin || ixx>xmax){
+      //printf("test HistToGraph rejecting ixx %e xmin %e xmax %e\n", ixx, xmin, xmax);
+      continue;
+    }
+
+    Double_t iex = 0;
+    if(herr){
+      iex = herr->GetBinContent(herr->GetXaxis()->FindBin(ixx));
+    }
+    else{
+      iex = hh->GetBinWidth(ii)/2.;
+    }
+
+    xxs[np] = ixx;
+    yys[np] = iyy;
+    exs[np] = iex;
+    eys[np] = iey;
+    np++;
+  }
+  TGraphAsymmErrors * gr = new TGraphAsymmErrors(np, xxs, yys, exs, exs, eys, eys);
+  gr->SetName(grname);
+  gr->SetMaximum(hh->GetMaximum());
+  gr->SetMinimum(hh->GetMinimum());
+  gr->GetXaxis()->SetLimits(hh->GetXaxis()->GetXmin(), hh->GetXaxis()->GetXmax());
+  
+  gr->SetLineColor(hh->GetLineColor());
+  gr->SetMarkerColor(hh->GetMarkerColor());
+  gr->SetFillStyle(hh->GetFillStyle());
+  gr->GetXaxis()->SetTitle(hh->GetXaxis()->GetTitle());
+  gr->GetYaxis()->SetTitle(hh->GetYaxis()->GetTitle());
+  return gr;
+}
+
+#endif
diff --git a/PWGPP/TPC/macros/PIDCalib/addMapToFile.C b/PWGPP/TPC/macros/PIDCalib/addMapToFile.C
new file mode 100644 (file)
index 0000000..dd1618e
--- /dev/null
@@ -0,0 +1,587 @@
+#include "TCanvas.h"
+#include "TH2D.h"
+#include "TFile.h"
+#include "TPRegexp.h"
+#include "TString.h"
+#include "TSystem.h"
+
+#include "AliOADBContainer.h"
+
+#include <iostream>
+
+//#define TEST
+
+Int_t addMapToFile(TString filePathNameMapToAdd, TString normalisation, TString period, Int_t pass, Bool_t isMC, Bool_t isSigma, 
+                   TString pathMapPackage = "etaMaps", TString fileNameMapPackage = "TPCetaMaps.root")
+{
+  //normalisation = "NoNormalisation", "SmallEtaNormalisation", "LargeEtaNormalisation"
+  
+  TString mapNameInFile = "";
+  TString par0StringName = "";
+  TString mapName = "";
+  TString mapTitle = "";
+  
+  Int_t* runLow = 0x0;
+  Int_t* runUp = 0x0;
+  Int_t nRunRanges = 1;
+  
+  TString dataType = "DATA";
+  if (isMC)
+    dataType = "MC";
+  
+  if (isSigma) {
+    mapName = "sigmaPar1Map";
+    //mapNameInFile = "hThetaMapSigmaPar1";
+    mapNameInFile = Form("hSigmaPar1_%s_extrapolated", normalisation.Data());
+    par0StringName = "c0";
+  }
+  else {
+    mapName = Form("TPCetaMaps_%s_pass%d", dataType.Data(), pass);
+    //mapNameInFile = Form("hRefined%s", normalisation.Data());
+    mapNameInFile = Form("hRes3DprimeFit_%s_extrapolated", normalisation.Data());
+  }
+  
+  
+  
+  
+  //TPRegexp reg(".*(LHC1[1-2][a-z]+[0-9]+[a-z_]*)/.*");
+  
+  
+  // Find the run range from the period
+  period.ToUpper();
+  
+  Bool_t addDefault = kFALSE;
+  TString defaultObj = Form("Default_%s_pass%d", dataType.Data(), pass);
+  
+  if (period.Contains("LHC10B") == 1 && !isMC) {
+    mapTitle = Form("LHC10b.pass%d", pass);
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 114650;
+    runUp[0] = 117630;
+  }
+  else if (period.Contains("LHC10C") == 1 && !isMC) {
+    mapTitle = Form("LHC10c.pass%d", pass);
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 117631;
+    runUp[0] = 121526;
+  }
+  else if (period.Contains("LHC10D") == 1 && !isMC) {
+    mapTitle = Form("LHC10d.pass%d", pass);
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 121527;
+    runUp[0] = 126460;
+  }
+  else if (period.Contains("LHC10E") == 1 && !isMC) {
+    mapTitle = Form("LHC10e.pass%d", pass);
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 126461;
+    runUp[0] = 130930;
+  }
+  else if (period.Contains("LHC11A_7TEV") == 1 && !isMC) {
+    //LHC11a -> 7TeV
+    mapTitle = Form("LHC11a_7TeV.pass%d", pass);
+  
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 139847;
+    runUp[0] = 146631;
+  }
+  else if (period.Contains("LHC11A_2.76TEV") == 1 && !isMC) {
+    //LHC11a -> 2.76TeV
+    mapTitle = Form("LHC11a_2.76TeV.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 146632;
+    runUp[0] = 146974;
+  }
+  else if (period.Contains("LHC10H") == 1 && !isMC) {
+    //LHC10h -> 2.76 ATeV (PbPb)
+    mapTitle = Form("LHC10h.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 136782;
+    runUp[0] = 139846;
+  }
+  else if (period.Contains("LHC11H") == 1 && !isMC) {
+    //LHC11h -> 2.76 ATeV (PbPb)
+    mapTitle = Form("LHC11h.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 165772;
+    runUp[0] = 170718;
+  }
+  else if (period.Contains("LHC10D1") == 1 && isMC) {
+    period = "LHC10D1";
+    mapTitle = Form("LHC10d1.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10b - LHC10c
+    runLow[0] = 114650;
+    runUp[0] = 121526;
+  }
+  else if (period.Contains("LHC10F6A") == 1 && isMC) {
+    period  = "LHC10F6A";
+    mapTitle = Form("LHC10f6a.pass%d", pass);
+    
+    nRunRanges = 3;
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10c - LHC10g
+    runLow[0] = 121527;
+    runUp[0] = 136781;
+    
+    // LHC11a_7TeV
+    runLow[1] = 139847;
+    runUp[1] = 146631;
+    
+    // NO LHC11a_2.76TeV (-> LHC11b10a)
+    
+    // LHC11b (146975-150721) + LHC11c (150722-155837) + LHC11d(155838-159649)
+    runLow[2] = 146975;
+    runUp[2] = 159649;
+  }
+  else if (period.Contains("LHC11B2") == 1 && isMC) {
+    period = "LHC11B2";
+    mapTitle = Form("LHC11b2.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10b - LHC10e
+    runLow[0] = 114650;
+    runUp[0] = 130930;
+  }
+  else if (period.Contains("LHC11B10A") == 1 && isMC) {
+    period = "LHC11B10A";
+    mapTitle = Form("LHC11b10a.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC11a, 2.76TeV
+    runLow[0] = 146632;
+    runUp[0] = 146974;
+  }
+  else if (period.Contains("LHC12F1") == 1 && isMC) {
+    period = "LHC12F1";
+    mapTitle = Form("LHC12f1.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC11a, 2.76TeV
+    runLow[0] = 146632;
+    runUp[0] = 146974;
+  }
+  //TODO Hope: Same map for 11A10A and 11A10B! Just use LHC11A10
+  else if (period.Contains("LHC11A10") == 1 && isMC) {
+    //LHC10h -> 2.76 ATeV (PbPb)
+    //TODO maybe also valid MC for LHC11h
+    period = "LHC11A10";
+    mapTitle = Form("LHC11a10.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10h
+    runLow[0] = 136782;
+    runUp[0] = 139846;
+  }
+  /*
+  else if (period.Contains("LHC11A10A") == 1 && isMC) {
+    //LHC10h -> 2.76 ATeV (PbPb)
+    period = "LHC11A10A";
+    mapTitle = Form("LHC11a10a.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10h
+    runLow[0] = 136782;
+    runUp[0] = 139846;
+  }
+  /*
+  else if (period.Contains("LHC11A10B") == 1 && isMC) {
+    //LHC10h -> 2.76 ATeV (PbPb)
+    period = "LHC11A10B";
+    mapTitle = Form("LHC11a10b.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    // LHC10h
+    runLow[0] = 136782;
+    runUp[0] = 139846;
+  }*/
+  else if (period.Contains("LHC12G") == 1 && isMC) {
+    //LHC12g (same map for 12g1, 12g4, ...) -> 5.023 ATeV (pPb)
+    period = "LHC12G";
+    mapTitle = Form("LHC12g.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    /*TODO look up?!
+    runLow[0] = 136782;
+    runUp[0] = 139846;*/
+  }
+  else if (period.Contains("LHC13B2_FIXN1") == 1 && isMC) {
+    //LHC13b2_fixn1 -> 5.023 ATeV (pPb)
+    period = "LHC13B2_FIXn1";
+    mapTitle = Form("LHC13b2_fixn1.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    /*
+     * runLow[0] = 194480;
+    runUp[0] = 196345;*/
+  }
+  else if (period.Contains("LHC13B2_FIX") == 1 && isMC) {
+    //LHC13b2_fix -> 5.023 ATeV (pPb)
+    period = "LHC13B2_FIX";
+    mapTitle = Form("LHC13b2_fix.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    /*
+     * runLow[0] = 194480;
+    runUp[0] = 196345;*/
+  }
+  else if (period.Contains("LHC13B") == 1 && !isMC) {
+    //LHC13a-d (beam only since 13b) periods -> 5.023 ATeV (pPb)
+    // -> High luminosity periods (e and following) require
+    // at the moment separate treatmeant
+    period = "LHC13B";
+    mapTitle = Form("LHC13b.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 194480;
+    runUp[0] = 195874;
+  }
+  else if (period.Contains("LHC13F") == 1 && !isMC) {
+    //LHC13e-f periods -> 5.023 ATeV (pPb) high luminosity
+    period = "LHC13F";
+    mapTitle = Form("LHC13f.pass%d", pass);
+    
+    runLow = new Int_t[nRunRanges];
+    runUp = new Int_t[nRunRanges];
+    
+    runLow[0] = 195875;
+    runUp[0] = 197411;
+  }
+  else if (period.Contains("DEFAULT")) {
+    mapTitle = Form("Default_%s_pass%d", dataType.Data(), pass);
+    addDefault = kTRUE;
+    printf("Request for default object...\n");
+#ifdef TEST
+  runLow = new Int_t[nRunRanges];
+  runUp = new Int_t[nRunRanges];
+  runLow[0] = 333333;
+  runUp[0] = 333333;
+#endif
+  }
+  else {
+    printf("Unknown period!\n");
+    return -1;
+  }
+  
+
+  
+  
+#ifdef TEST
+  // TEST created file
+  printf("\n\n***********TEST MODE ENABLED*********\n\n");
+  Int_t fRun = (runUp[0] + runLow[0]) / 2.0;
+  TH2D* etaMap = 0x0;
+  TH2D* etaSigmaPar1Map = 0x0;
+  
+  Double_t sigmaPar0 = -1.0;
+  
+  // Load the eta correction maps
+  AliOADBContainer etaMapsCont2(Form("TPCetaMaps_%s_pass%d", dataType.Data(), pass)); 
+    
+  Int_t statusCont2 = etaMapsCont2.InitFromFile("etaMaps/TPCetaMaps.root", Form("TPCetaMaps_%s_pass%d", dataType.Data(), pass));
+  if (statusCont2) {
+    printf("Failed initializing TPC eta correction maps from OADB");
+    return -1;
+  }
+  else {
+    if (isMC) {
+      TString searchMap = Form("TPCetaMaps_%s_%s_pass%d", dataType.Data(), period.Data(), pass);
+      etaMap = dynamic_cast<TH2D *>(etaMapsCont2.GetDefaultObject(searchMap.Data()));
+      if (!etaMap) {
+        // Try default object
+        etaMap = dynamic_cast<TH2D *>(etaMapsCont2.GetDefaultObject(defaultObj.Data()));
+        
+        if (!etaMap) {
+          printf("TPC eta correction map not found for period %s and also no default map found -> Disabled eta correction!!!",
+                 period.Data());
+          return -1;
+        }
+      }
+    }
+    else {
+      etaMap = dynamic_cast<TH2D *>(etaMapsCont2.GetObject(fRun, defaultObj.Data()));
+      if (!etaMap) {
+        printf("TPC eta correction map not found for run %d -> Disabled eta correction!!!", fRun);
+        return -1;
+      }
+    }
+  }
+  
+  // Load the sigma parametrisation (1/dEdx vs tanTheta_local (~eta))
+  AliOADBContainer etaSigmaMapsCont2(Form("TPCetaSigmaMaps_%s_pass%d", dataType.Data(), pass)); 
+  
+  statusCont2 = etaSigmaMapsCont2.InitFromFile("etaMaps/TPCetaMaps.root", Form("TPCetaSigmaMaps_%s_pass%d", dataType.Data(), pass));
+  if (statusCont2) {
+    printf("Failed initializing TPC eta sigma maps from OADB");
+    return -1;
+  }
+  else {
+    TObjArray* etaSigmaPars = 0x0;
+    if (isMC) {
+      TString searchMap = Form("TPCetaSigmaMaps_%s_%s_pass%d", dataType.Data(), period.Data(), pass);
+      etaSigmaPars = dynamic_cast<TObjArray *>(etaSigmaMapsCont2.GetDefaultObject(searchMap.Data()));
+      if (!etaSigmaPars) {
+        // Try default object
+        etaSigmaPars = dynamic_cast<TObjArray *>(etaSigmaMapsCont2.GetDefaultObject(defaultObj.Data()));
+        if (!etaSigmaPars) {
+          printf("TPC eta sigma parametrisation not found for period %s and also no default parametrisation found -> Using old sigma parametrisation!!!",
+                 period.Data());
+          return -1;
+        }
+      }
+    }
+    else {
+      etaSigmaPars = dynamic_cast<TObjArray *>(etaSigmaMapsCont2.GetObject(fRun, defaultObj.Data()));
+      if (!etaSigmaPars) {
+        printf("TPC eta sigma parametrisation not found for run %d -> Using old sigma parametrisation!!!", fRun);
+        return -1;
+      }
+    }
+    
+    etaSigmaPar1Map = dynamic_cast<TH2D *>(etaSigmaPars->FindObject("sigmaPar1Map"));
+    TNamed* sigmaPar0Info = dynamic_cast<TNamed *>(etaSigmaPars->FindObject("sigmaPar0"));
+    
+    if (sigmaPar0Info) {
+      TString sigmaPar0String = sigmaPar0Info->GetTitle();
+      sigmaPar0 = sigmaPar0String.Atof();
+    }
+    else {
+      // Something is weired because the object for parameter 0 could not be loaded -> New sigma parametrisation can not be used!
+      etaSigmaPar1Map = 0x0;
+      return -1;
+    }
+  }
+  
+  printf("\n\nLoaded c0: %.4f\n", sigmaPar0);
+  TCanvas* c = new TCanvas();
+  c->Divide(2,1);
+  c->cd(1);
+  etaMap->Draw("colz");
+  c->cd(2);
+  etaSigmaPar1Map->Draw("colz");
+  
+  return 0;
+#endif
+  
+  
+  
+  
+  
+  
+  
+  
+  // Open the map package and retrieve the OADBContainers. If the map package does not exist,
+  // create it. If there is no OADBContainer yet, create a new one
+  TString filePathNameMapPackage = gSystem->ExpandPathName(Form("%s/%s", pathMapPackage.Data(), fileNameMapPackage.Data()));
+  AliOADBContainer* etaMapsCont = 0x0;
+  
+  if (!isSigma) {
+    etaMapsCont = new AliOADBContainer(Form("TPCetaMaps_%s_pass%d", dataType.Data(), pass)); 
+    Int_t statusCont = etaMapsCont->InitFromFile(filePathNameMapPackage.Data(), Form("TPCetaMaps_%s_pass%d", dataType.Data(), pass));
+    if (statusCont) 
+      printf("No OADBContainer for the current settings found - creating a new one...\n");
+  }
+  else {
+    etaMapsCont = new AliOADBContainer(Form("TPCetaSigmaMaps_%s_pass%d", dataType.Data(), pass)); 
+    Int_t statusCont = etaMapsCont->InitFromFile(filePathNameMapPackage.Data(), Form("TPCetaSigmaMaps_%s_pass%d", dataType.Data(), pass));
+    if (statusCont) 
+      printf("No OADBContainer for the current settings found - creating a new one...\n");
+  }
+
+
+  // Open the map that should be added
+  filePathNameMapToAdd = gSystem->ExpandPathName(filePathNameMapToAdd.Data());
+  
+  TFile* fMapToAdd = TFile::Open(filePathNameMapToAdd.Data());
+  
+  if (!fMapToAdd) {
+    std::cout << "Failed to open map file \"" << filePathNameMapToAdd.Data() << "\"!" << std::endl;
+    return -1;
+  }
+    
+  TH2D* hMap = dynamic_cast<TH2D*>(fMapToAdd->Get(mapNameInFile.Data()));
+  if (!hMap) {
+    std::cout << "Failed to load map!" << std::endl;
+  
+    return -1;
+  }
+  
+  hMap->SetName(mapName.Data());
+  hMap->SetTitle(mapTitle.Data());
+  
+  TNamed* c0Info = 0x0;
+  TObjArray* sigmaPars = 0x0;
+  
+  if (isSigma) {
+    c0Info = dynamic_cast<TNamed*>(fMapToAdd->Get(par0StringName.Data()));
+    if (!c0Info) {
+      std::cout << "Failed to load parameter 0!" << std::endl;
+      return -1;
+    }
+    
+    c0Info->SetName("sigmaPar0");
+    
+    sigmaPars = new TObjArray(2);
+    sigmaPars->SetOwner(kTRUE);
+    
+    if (isMC) {
+      sigmaPars->SetName(Form("TPCetaSigmaMaps_%s_%s_pass%d", dataType.Data(), period.Data(), pass));
+    }
+    else {
+      sigmaPars->SetName(Form("TPCetaSigmaMaps_%s_pass%d", dataType.Data(), pass));
+    }
+    
+    sigmaPars->Add(hMap);
+    sigmaPars->Add(c0Info);
+    
+    if (addDefault) {
+      sigmaPars->SetName(defaultObj.Data());
+      if (isMC) {
+        TObjArray* oldObj = (TObjArray*)etaMapsCont->GetDefaultList()->Remove(etaMapsCont->GetDefaultObject(sigmaPars->GetName()));
+        if (oldObj)
+          oldObj->Delete();
+        delete oldObj;
+      }
+      else {
+        etaMapsCont->CleanDefaultList();
+      }
+      etaMapsCont->AddDefaultObject(sigmaPars);
+    }
+    else {
+      if (isMC) {
+        TObjArray* oldObj = (TObjArray*)etaMapsCont->GetDefaultList()->Remove(etaMapsCont->GetDefaultObject(sigmaPars->GetName()));
+        if (oldObj) {
+          printf("Updating existing object \"%s\"...\n", mapTitle.Data());
+          oldObj->Delete();
+          delete oldObj;
+        }
+        else {
+          printf("Creating new object \"%s\"...\n", mapTitle.Data());
+        }
+        etaMapsCont->AddDefaultObject(sigmaPars);
+      }
+      else {
+        // Check, if the object already exists by taking the center of the run range.
+        // If there is a conflict for the range, AliOADBContainer will give an AliFatal.
+        for (Int_t range = 0; range < nRunRanges; range++) {
+          Int_t index = etaMapsCont->GetIndexForRun((runUp[range] + runLow[range]) / 2.0);
+          if (index < 0) {
+            printf("Creating new object for run range %d - %d...\n", runLow[range], runUp[range]);
+            etaMapsCont->AppendObject((range == 0) ? sigmaPars : sigmaPars->Clone(), runLow[range], runUp[range]);
+          }
+          else {
+            printf("Updating existing object for run range %d - %d...\n", runLow[range], runUp[range]);
+            etaMapsCont->UpdateObject(index, (range == 0) ? sigmaPars : sigmaPars->Clone(), runLow[range], runUp[range]);
+          }
+        }
+      }
+    }
+  }
+  else {
+    
+    if (isMC) {
+      hMap->SetName(Form("TPCetaMaps_%s_%s_pass%d", dataType.Data(), period.Data(), pass));
+    }
+    
+    if (addDefault) {
+      hMap->SetName(defaultObj.Data());
+      
+      if (isMC) {
+        TH2D* oldMap = (TH2D*)etaMapsCont->GetDefaultList()->Remove(etaMapsCont->GetDefaultObject(hMap->GetName()));
+        delete oldMap;
+      }
+      else {
+        etaMapsCont->CleanDefaultList();
+      }
+      etaMapsCont->AddDefaultObject(hMap);
+    }
+    else {
+      if (isMC) {
+        TH2D* oldMap = (TH2D*)etaMapsCont->GetDefaultList()->Remove(etaMapsCont->GetDefaultObject(hMap->GetName()));
+        if (oldMap) {
+          printf("Updating existing object \"%s\"...\n", mapTitle.Data());
+          delete oldMap;
+        }
+        else {
+          printf("Creating new object \"%s\"...\n", mapTitle.Data());
+        }
+        etaMapsCont->AddDefaultObject(hMap);
+      }
+      else {
+        // Check, if the object already exists by taking the center of the run range.
+        // If there is a conflict for the range, AliOADBContainer will give an AliFatal.
+        for (Int_t range = 0; range < nRunRanges; range++) {
+          Int_t index = etaMapsCont->GetIndexForRun((runUp[range] + runLow[range]) / 2.0);
+          if (index < 0) {
+            printf("Creating new object for run range %d - %d...\n", runLow[range], runUp[range]);
+            etaMapsCont->AppendObject((range == 0) ? hMap : hMap->Clone(), runLow[range], runUp[range]);
+          }
+          else {
+            printf("Updating existing object for run range %d - %d...\n", runLow[range], runUp[range]);
+            etaMapsCont->UpdateObject(index, (range == 0) ? hMap : hMap->Clone(), runLow[range], runUp[range]);
+          }
+        }
+      }
+    }
+  }
+
+  //etaMapsCont->WriteToFile(filePathNameMapPackage.Data());
+  // Does the same as the WriteToFile function, but allow for using "kOverwrite" to save memory!
+  TFile* f = new TFile(filePathNameMapPackage.Data(), "update");
+  f->Delete(etaMapsCont->GetName());
+  etaMapsCont->Write(0, TObject::kOverwrite);
+  f->Purge();
+  f->Close();
+  
+  
+  gSystem->Exec(Form("echo \"%s%s:\t\t%s#%s\n\" >> %s/UsedFilesForMap.txt", mapTitle.Data(), isSigma ? " (sigma)" : " (etaCorr)",
+                     filePathNameMapToAdd.Data(), mapNameInFile.Data(), pathMapPackage.Data()));
+  
+  fMapToAdd->Close();
+  
+  printf("\n****************Successfully added map \"%s\"!\n\n\n\n", mapTitle.Data());
+  return 0;
+}
\ No newline at end of file
diff --git a/PWGPP/TPC/macros/PIDCalib/checkPullTree.C b/PWGPP/TPC/macros/PIDCalib/checkPullTree.C
new file mode 100644 (file)
index 0000000..8d77feb
--- /dev/null
@@ -0,0 +1,766 @@
+#include "TTree.h"
+#include "TH2D.h"
+#include "TH3D.h"
+#include "TH3F.h"
+#include "TCanvas.h"
+#include "TStyle.h"
+#include "TList.h"
+#include "TString.h"
+#include "TFile.h"
+#include "TMath.h"
+#include "TDatime.h"
+#include "TRandom3.h"
+#include "TSpline.h"
+
+#include "TF1.h" //TODO NOW
+
+#include <iostream>
+#include <iomanip>
+
+#include "THnSparseDefinitions.h"
+
+//__________________________________________________________________________________________
+void normaliseHisto(TH2D* h)
+{
+  h->Sumw2();
+  //TODO NOW: Disabled normalisation (doesn't make sense here)
+  /*
+  for (Int_t x = 1; x <= h->GetXaxis()->GetNbins(); x++) {
+    Double_t binWidth = h->GetXaxis()->GetBinWidth(x);
+    for (Int_t y = 1; y <= h->GetYaxis()->GetNbins(); y++) {
+      Double_t binError = h->GetBinError(x,y);
+      h->SetBinContent(x, y, h->GetBinContent(x, y) / binWidth);
+      h->SetBinError(x, y, binError / binWidth);
+    }
+  }*/
+}
+
+
+//_________________________________________________________________________________________
+Int_t getBinX(TH2D* h, Double_t tanTheta)
+{
+  Int_t binX = h->GetXaxis()->FindBin(tanTheta);
+  
+  if (binX <= 0)
+    binX = 1;
+  if (binX > h->GetXaxis()->GetNbins())
+    binX = h->GetXaxis()->GetNbins();
+
+    return binX;
+}
+
+
+//_________________________________________________________________________________________
+Int_t getBinY(TH2D* h, Double_t dEdxInv)
+{
+  Int_t binY = h->GetYaxis()->FindBin(dEdxInv);
+  
+  if (binY <= 0)
+    binY = 1;
+  if (binY > h->GetYaxis()->GetNbins())
+    binY = h->GetYaxis()->GetNbins();
+
+  return binY;
+}
+
+
+//_________________________________________________________________________________________
+Int_t checkPullTree(TString pathTree,  TString pathNameThetaMap, TString pathNameSigmaMap,
+                    TString mapSuffix, const Int_t collType /*0: pp, 1: pPb, 2: PbPb*/, const Bool_t plotPull = kTRUE,
+                    const Double_t downScaleFactor = 1,
+                    TString pathNameSplinesFile = "", TString prSplinesName = "",
+                    TString fileNameTree = "bhess_PIDetaTree.root", TString treeName = "fTree")
+{
+  const Bool_t isNonPP = collType != 0;
+  const Double_t massProton = AliPID::ParticleMass(AliPID::kProton);
+  
+  Bool_t recalculateExpecteddEdx = pathNameSplinesFile != "";
+  
+  TFile* f = 0x0;
+       
+  f = TFile::Open(Form("%s/%s", pathTree.Data(), fileNameTree.Data()));
+  if (!f)  {
+    std::cout << "Failed to open tree file \"" << Form("%s/%s", pathTree.Data(), fileNameTree.Data()) << "\"!" << std::endl;
+    return -1;
+  }
+      
+  // Extract the data Tree
+  TTree* tree = dynamic_cast<TTree*>(f->Get(treeName.Data()));
+  if (!tree) {
+    std::cout << "Failed to load data tree!" << std::endl;
+    return -1;
+  }
+  
+  // Extract the splines, if desired
+  TSpline3* splPr = 0x0;
+  if (recalculateExpecteddEdx) {
+    std::cout << "Loading splines to recalculate expected dEdx!" << std::endl << std::endl;
+    
+    TFile* fSpl = TFile::Open(pathNameSplinesFile.Data());
+    if (!fSpl) {
+      std::cout << "Failed to open spline file \"" << pathNameSplinesFile.Data() << "\"!" << std::endl;
+      return 0x0;
+    }
+    
+    TObjArray* TPCPIDResponse = (TObjArray*)fSpl->Get("TPCPIDResponse");
+    if (!TPCPIDResponse) {
+      splPr = (TSpline3*)fSpl->Get(prSplinesName.Data());
+      
+      // If splines are in file directly, without TPCPIDResponse object, try to load them
+      if (!splPr) {
+        std::cout << "Failed to load object array from spline file \"" << pathNameSplinesFile.Data() << "\"!" << std::endl;
+        return 0x0;
+      }
+    }
+    else {
+      splPr = (TSpline3*)TPCPIDResponse->FindObject(prSplinesName.Data());
+      
+      if (!splPr) {
+        std::cout << "Failed to load splines from file \"" << pathNameSplinesFile.Data() << "\"!" << std::endl;
+        return 0x0;
+      }
+    }
+  }
+  else
+    std::cout << "Taking dEdxExpected from Tree..." << std::endl << std::endl;
+
+  // Extract the correction maps
+  TFile* fMap = TFile::Open(pathNameThetaMap.Data());
+  if (!fMap)  {
+    std::cout << "Failed to open thetaMap file \"" << pathNameThetaMap.Data() << "\"! Will not additionally correct data...." << std::endl;
+  }
+
+  TH2D* hMap = 0x0;
+  
+  if (fMap) {
+    hMap = dynamic_cast<TH2D*>(fMap->Get(Form("hRefined%s", mapSuffix.Data())));
+    if (!hMap) {
+      std::cout << "Failed to load theta map!" << std::endl;
+      return -1;
+    }
+  }
+
+  TFile* fSigmaMap = TFile::Open(pathNameSigmaMap.Data());
+  if (!fSigmaMap)  {
+    std::cout << "Failed to open simgaMap file \"" << pathNameSigmaMap.Data() << "\"!" << std::endl;
+    return -1;
+  }
+
+  TH2D* hThetaMapSigmaPar1 = dynamic_cast<TH2D*>(fSigmaMap->Get("hThetaMapSigmaPar1"));
+  if (!hThetaMapSigmaPar1) {
+    std::cout << "Failed to load sigma map for par 1!" << std::endl;
+    return -1;
+  }
+
+  Double_t c0 = -1;
+  TNamed* c0Info = dynamic_cast<TNamed*>(fSigmaMap->Get("c0"));
+  if (!c0Info) {
+    std::cout << "Failed to extract c0 from file with sigma map!" << std::endl;
+    return -1;
+  }
+
+  TString c0String = c0Info->GetTitle();
+  c0 = c0String.Atof();
+  printf("Loaded parameter 0 for sigma: %f\n\n", c0);
+
+  if (plotPull)
+    std::cout << "Plotting pull..." << std::endl << std::endl;
+  else
+    std::cout << "Plotting delta'..." << std::endl << std::endl;
+
+  Long64_t nTreeEntries = tree->GetEntriesFast();
+
+  Double_t dEdx = 0.; // Measured dE/dx
+  Double_t dEdxExpected = 0.; // Expected dE/dx according to parametrisation
+  Double_t tanTheta = 0.; // Tangens of (local) theta at TPC inner wall
+  Double_t pTPC = 0.; // Momentum at TPC inner wall
+  UShort_t tpcSignalN = 0; // Number of clusters used for dEdx
+  UChar_t  pidType = 0;
+  Int_t    fMultiplicity = 0;
+  //Double_t phiPrime = 0;
+
+  // Only activate the branches of interest to save processing time
+  tree->SetBranchStatus("*", 0); // Disable all branches
+  tree->SetBranchStatus("pTPC", 1);
+  tree->SetBranchStatus("dEdx", 1);
+  tree->SetBranchStatus("dEdxExpected", 1);
+  tree->SetBranchStatus("tanTheta", 1);
+  tree->SetBranchStatus("tpcSignalN", 1);
+  tree->SetBranchStatus("pidType", 1);
+  //tree->SetBranchStatus("phiPrime", 1);
+  if (isNonPP)
+    tree->SetBranchStatus("fMultiplicity", 1);
+
+  
+  tree->SetBranchAddress("dEdx", &dEdx);
+  tree->SetBranchAddress("dEdxExpected", &dEdxExpected);
+  tree->SetBranchAddress("tanTheta", &tanTheta);
+  tree->SetBranchAddress("tpcSignalN", &tpcSignalN);
+  tree->SetBranchAddress("pTPC", &pTPC);
+  tree->SetBranchAddress("pidType", &pidType);
+  //tree->SetBranchAddress("phiPrime", &phiPrime);
+  if (isNonPP)
+    tree->SetBranchAddress("fMultiplicity", &fMultiplicity);
+
+  
+  // Output file
+  TDatime daTime;
+  TString savefileName = Form("%s%s_checkPullSigma_%04d_%02d_%02d__%02d_%02d.root", fileNameTree.ReplaceAll(".root", "").Data(),
+                              recalculateExpecteddEdx ? "_recalcdEdx" : "",
+                              daTime.GetYear(), daTime.GetMonth(), daTime.GetDay(), daTime.GetHour(), daTime.GetMinute());
+
+  TFile* fSave = TFile::Open(Form("%s/%s", pathTree.Data(), savefileName.Data()), "recreate");
+  if (!fSave) {
+    std::cout << "Failed to open save file \"" << Form("%s/%s", pathTree.Data(), savefileName.Data()) << "\"!" << std::endl;
+    return -1;
+  }
+  
+  const Double_t pBoundLow = 0.1;
+  const Double_t pBoundUp = 5;
+
+  const Int_t nBins1 = TMath::Ceil(180 / downScaleFactor);
+  const Int_t nBins2 = TMath::Ceil(100 / downScaleFactor);
+  const Int_t nBins3 = TMath::Ceil(60 / downScaleFactor);
+  
+  const Int_t nPbinsForMap = nBins1 + nBins2 + nBins3;
+  Double_t binsPforMap[nPbinsForMap + 1];
+  
+  Double_t binWidth1 = (1.0 - pBoundLow) / nBins1;
+  Double_t binWidth2 = (2.0 - 1.0 ) / nBins2;
+  Double_t binWidth3 = (pBoundUp - 2.0) / nBins3;
+  
+  for (Int_t i = 0; i < nBins1; i++)  {
+    binsPforMap[i] = pBoundLow + i * binWidth1;
+  }
+  for (Int_t i = nBins1, j = 0; i < nBins1 + nBins2; i++, j++)  {
+    binsPforMap[i] = 1.0 + j * binWidth2;
+  }
+  for (Int_t i = nBins1 + nBins2, j = 0; i < nBins1 + nBins2 + nBins3; i++, j++)  {
+    binsPforMap[i] = 2.0 + j * binWidth3;
+  }
+  binsPforMap[nPbinsForMap] = pBoundUp;
+
+  TH2D* hPull = new TH2D("hPull", "Pull vs. p_{TPC} integrated over tan(#Theta);p_{TPC} (GeV/c);Pull", nPbinsForMap, binsPforMap, 
+                         plotPull ? 120 : 240, plotPull ? -6 : -0.6, plotPull ? 6 : 0.6);
+  TH2D* hPullAdditionalCorr = (TH2D*)hPull->Clone("hPullAdditionalCorr");
+  hPullAdditionalCorr->SetTitle("Pull vs. p_{TPC} integrated over tan(#Theta) with additional dEdx correction w.r.t. tan(#Theta)");
+  /*
+  const Int_t nThetaHistos = 3;
+  TH2D* hPullTheta[nThetaHistos];
+  TH2D* hPullAdditionalCorrTheta[nThetaHistos];
+  Double_t tThetaLow[nThetaHistos] = { 0.0, 0.4, 0.9 };
+  Double_t tThetaHigh[nThetaHistos] = { 0.1, 0.5, 1.0 };
+  */
+  const Int_t nThetaHistos = 10;
+  TH2D* hPullTheta[nThetaHistos];
+  TH2D* hPullAdditionalCorrTheta[nThetaHistos];
+  Double_t tThetaLow[nThetaHistos] = { 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 };
+  Double_t tThetaHigh[nThetaHistos] = { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 };
+  
+
+  for (Int_t i = 0; i < nThetaHistos; i++)    {
+    hPullTheta[i] = new TH2D(Form("hPullTheta_%d", i),
+                             Form("Pull vs. p_{TPC} for %.2f <= |tan(#Theta)| < %.2f;p_{TPC} (GeV/c);Pull", tThetaLow[i], tThetaHigh[i]),
+                             nPbinsForMap, binsPforMap, plotPull ? 120 : 240, plotPull ? -6 : -0.6, plotPull ? 6 : 0.6);
+
+    hPullAdditionalCorrTheta[i] =
+      new TH2D(Form("hPullAdditionalCorrTheta_%d", i),
+               Form("Pull vs. p_{TPC} for %.2f <= |tan(#Theta)| < %.2f with additional dEdx correction w.r.t. tan(#Theta);p_{TPC} (GeV/c);Pull",
+                    tThetaLow[i], tThetaHigh[i]),
+               nPbinsForMap, binsPforMap, plotPull ? 120 : 240, plotPull ? -6 : -0.6, plotPull ? 6 : 0.6);
+  }
+
+  
+  
+  
+  
+  
+  TF1 corrFuncMult("corrFuncMult", "[0] + [1]*TMath::Max([4], TMath::Min(x, [3])) + [2] * TMath::Power(TMath::Max([4], TMath::Min(x, [3])), 2)",
+                   0., 0.2);
+  TF1 corrFuncMultTanTheta("corrFuncMultTanTheta", "[0] * (x -[2]) + [1] * (x * x - [2] * [2])", -1.5, 1.5);
+  TF1 corrFuncSigmaMult("corrFuncSigmaMul", "TMath::Max(0, [0] + [1]*TMath::Min(x, [3]) + [2] * TMath::Power(TMath::Min(x, [3]), 2))", 0., 0.2);
+  
+  
+  // LHC13b.pass2
+  if (isNonPP)
+    printf("Using corr Parameters for 13b.pass2\n!");
+  
+  corrFuncMult.SetParameter(0, -5.906e-06);
+  corrFuncMult.SetParameter(1, -5.064e-04);
+  corrFuncMult.SetParameter(2, -3.521e-02);
+  corrFuncMult.SetParameter(3,  2.469e-02);
+  corrFuncMult.SetParameter(4, 0);
+  
+  corrFuncMultTanTheta.SetParameter(0, -5.32e-06);
+  corrFuncMultTanTheta.SetParameter(1,  1.177e-05);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, 0.);
+  corrFuncSigmaMult.SetParameter(1, 0.);
+  corrFuncSigmaMult.SetParameter(2, 0.);
+  corrFuncSigmaMult.SetParameter(3, 0.);
+  
+  
+  /* OK, but PID task was not very satisfying
+  corrFuncMult.SetParameter(0, -6.27187e-06);
+  corrFuncMult.SetParameter(1, -4.60649e-04);
+  corrFuncMult.SetParameter(2, -4.26450e-02);
+  corrFuncMult.SetParameter(3, 2.40590e-02);
+  corrFuncMult.SetParameter(4, 0);
+  
+  corrFuncMultTanTheta.SetParameter(0, -5.338e-06);
+  corrFuncMultTanTheta.SetParameter(1,  1.220e-05);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, 7.89237e-05);
+  corrFuncSigmaMult.SetParameter(1, -1.30662e-02);
+  corrFuncSigmaMult.SetParameter(2, 8.91548e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.47931e-02);
+  */
+  
+  
+  /*
+  // LHC11a10a
+  if (isNonPP)
+    printf("Using corr Parameters for 11a10a\n!");
+  
+  corrFuncMult.SetParameter(0, 6.90133e-06);
+  corrFuncMult.SetParameter(1, -1.22123e-03);
+  corrFuncMult.SetParameter(2, 1.80220e-02);
+  corrFuncMult.SetParameter(3, 0.1);
+  corrFuncMult.SetParameter(4, 6.45306e-03);
+  
+  corrFuncMultTanTheta.SetParameter(0, -2.85505e-07);
+  corrFuncMultTanTheta.SetParameter(1, -1.31911e-06);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, -4.29665e-05);
+  corrFuncSigmaMult.SetParameter(1, 1.37023e-02);
+  corrFuncSigmaMult.SetParameter(2, -6.36337e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.13479e-02);
+  */
+  
+  /* OLD without saturation and large error for negative slopes
+  corrFuncSigmaMult.SetParameter(0, -4.79684e-05);
+  corrFuncSigmaMult.SetParameter(1, 1.49938e-02);
+  corrFuncSigmaMult.SetParameter(2, -7.15269e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.06855e-02);
+  */
+  
+  /* OLD very good try, but with fewer pBins for the fitting
+  corrFuncMult.SetParameter(0, 6.88365e-06);
+  corrFuncMult.SetParameter(1, -1.22324e-03);
+  corrFuncMult.SetParameter(2, 1.81625e-02);
+  corrFuncMult.SetParameter(3, 0.1);
+  corrFuncMult.SetParameter(4, 6.36890e-03);
+  
+  corrFuncMultTanTheta.SetParameter(0, -2.85505e-07);
+  corrFuncMultTanTheta.SetParameter(1, -1.31911e-06);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, -4.28401e-05);
+  corrFuncSigmaMult.SetParameter(1, 1.24812e-02);
+  corrFuncSigmaMult.SetParameter(2, -5.28531e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.25147e-02);
+  */
+  /*OLD good try
+  corrFuncMult.SetParameter(0, 7.50321e-06);
+  corrFuncMult.SetParameter(1, -1.25250e-03);
+  corrFuncMult.SetParameter(2, 1.85437e-02);
+  corrFuncMult.SetParameter(3, 0.1);
+  corrFuncMult.SetParameter(4, 6.21192e-03);
+  
+  corrFuncMultTanTheta.SetParameter(0, -1.43112e-07);
+  corrFuncMultTanTheta.SetParameter(1, -1.53e-06);
+  corrFuncMultTanTheta.SetParameter(2, 0.3);
+  
+  corrFuncSigmaMult.SetParameter(0, -2.54019e-05);
+  corrFuncSigmaMult.SetParameter(1, 8.68883e-03);
+  corrFuncSigmaMult.SetParameter(2, -3.36176e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.29230e-02);
+  */
+  
+  /*
+  // LHC10h.pass2
+  if (isNonPP)
+    printf("Using corr Parameters for 10h.pass2\n!");
+  
+  corrFuncMult.SetParameter(0, 3.21636e-07);
+  corrFuncMult.SetParameter(1, -6.65876e-04);
+  corrFuncMult.SetParameter(2, 1.28786e-03);
+  corrFuncMult.SetParameter(3, 1.47677e-02);
+  corrFuncMult.SetParameter(4, 0.);
+  
+  corrFuncMultTanTheta.SetParameter(0, 7.23591e-08);
+  corrFuncMultTanTheta.SetParameter(1, 2.7469e-06);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, -1.22590e-05);
+  corrFuncSigmaMult.SetParameter(1, 6.88888e-03);
+  corrFuncSigmaMult.SetParameter(2, -3.20788e-01);
+  corrFuncSigmaMult.SetParameter(3, 1.07345e-02);
+  */
+  
+  /*OLD bad try
+  corrFuncMult.SetParameter(0, 2.71514e-07);
+  corrFuncMult.SetParameter(1, -6.92031e-04);
+  corrFuncMult.SetParameter(2, 3.56042e-03);
+  corrFuncMult.SetParameter(3, 1.47497e-02);
+  corrFuncMult.SetParameter(4, 0.);
+  
+  corrFuncMultTanTheta.SetParameter(0, 8.53204e-08);
+  corrFuncMultTanTheta.SetParameter(1, 2.85591e-06);
+  corrFuncMultTanTheta.SetParameter(2, -0.5);
+  
+  corrFuncSigmaMult.SetParameter(0, -6.82477e-06);
+  corrFuncSigmaMult.SetParameter(1, 4.97051e-03);
+  corrFuncSigmaMult.SetParameter(2, -1.64954e-01);
+  corrFuncSigmaMult.SetParameter(3, 9.21061e-03);
+  */
+  
+
+  //TODO NOW
+  TF1* fShapeSmallP = new TF1("fShapeSmallP", "pol5", -0.4, 0.4);
+  fShapeSmallP->SetParameters(1.01712, -0.0202725, -0.260692, 0.261623, 0.671854, -1.14014);
+    
+  for (Long64_t i = 0; i < nTreeEntries; i++) {
+    tree->GetEntry(i);
+
+    if (dEdx <= 0 || dEdxExpected <= 0 || tpcSignalN <= 10)
+      continue;
+    /*
+    Double_t pT = pTPC*TMath::Sin(-TMath::ATan(tanTheta)+TMath::Pi()/2.0);
+    if ((phiPrime > 0.072/pT+TMath::Pi()/18.0-0.035 && phiPrime < 0.07/pT/pT+0.1/pT+TMath::Pi()/18.0+0.035)) 
+      continue;
+    */
+      
+    if (pidType != kMCid) {
+      if (pidType == kTPCid && pTPC > 0.6)
+        continue;
+      if (pidType == kTPCandTOFid && (pTPC < 0.6 || pTPC > 2.0))
+        continue;
+      if ((collType == 2) && pidType == kTPCandTOFid && pTPC > 1.0)
+        continue;// Only V0's in case of PbPb above 1.0 GeV/c
+      if (pidType == kV0idPlusTOFrejected) //TODO NOW NEW
+        continue;
+    }
+    
+    if (recalculateExpecteddEdx) {
+      dEdxExpected = 50. * splPr->Eval(pTPC / massProton); //WARNING: What, if MIP is different from 50.? Seems not to be used (tested for pp, MC_pp, PbPb and MC_PbPb), but can in principle happen
+    }
+      
+    //TODO NOW
+    /*
+    if (TMath::Abs(tanTheta) <= 0.4) {
+      Double_t p0 = fShapeSmallP->Eval(tanTheta) - 1.0; // Strength of the correction
+      Double_t p1 = -9.0; // How fast the correction is turned off
+      Double_t p2 = -0.209; // Turn off correction around 0.2 GeV/c
+      Double_t p3 = 1.0; // Delta' for large p should be 1
+
+      Double_t corrFactor = TMath::Erf((pTPC + p2) * p1) * p0 + p3 + p0; // Add p0 to have 1 for p3 = 1 and large pTPC
+      dEdxExpected *= corrFactor;
+    }*/
+    
+     /*TODO old unsuccessful try 
+    Double_t thetaGlobalTPC = -TMath::ATan(tanTheta) + TMath::Pi() / 2.;
+    Double_t pTtpc = pTPC * TMath::Sin(thetaGlobalTPC);
+    Double_t pTtpcInv = (pTtpc > 0) ? 1. / pTtpc : 0;
+    Double_t p0 = 1.0;
+    Double_t p1 = 1./ 0.5;//TODO 2.0;
+    Double_t p2 = -0.2;//TODO 0.1
+    Double_t pTcorrFactor = p0 + (pTtpcInv > p1) * p2 * (pTtpcInv - p1);
+    
+    dEdxExpected *= pTcorrFactor;
+    */
+    
+      
+    // From the momentum (via dEdxExpected) and the tanTheta of the track, the expected dEdx can be calculated (correctedDeDxExpected).
+    // If the splines are correct, this should give in average the same value as dEdx. 
+    // Now valid: Maps created from corrected data with splines adopted to corrected data, so lookup should be for dEdxExpected=dEdxSplines (no further
+    // eta correction) or the corrected dEdx from the track (which should ideally be = dEdxSplines)
+    
+    // Tested with corrected data for LHC10d.pass2: using dEdx for the lookup (which is the corrected value and should ideally be = dEdxSplines):
+    // Results almost the same. Maybe slightly better for dEdxExpected.
+    
+    // No longer valid: Note that the maps take always the uncorrected dEdx w.r.t.
+    // tanTheta, so that correctedDeDxExpected is needed here normally. However, the information for the correction will be lost at some point.
+    // Therefore, dEdxExpected can be used instead and should provide a good approximation.
+    Double_t c1FromSigmaMap = hThetaMapSigmaPar1->GetBinContent(getBinX(hThetaMapSigmaPar1, tanTheta), getBinY(hThetaMapSigmaPar1, 1./dEdxExpected));
+    
+    Double_t expectedSigma = dEdxExpected * TMath::Sqrt( c0 * c0 + (c1FromSigmaMap * c1FromSigmaMap) / tpcSignalN);
+    Double_t pull = (dEdx - dEdxExpected) / (plotPull ? expectedSigma: dEdxExpected);
+
+    // Fill pull histo
+    hPull->Fill(pTPC, pull);
+
+    Double_t tanThetaAbs = TMath::Abs(tanTheta);
+    
+    for (Int_t j = 0; j < nThetaHistos; j++)    {
+      if (tanThetaAbs  >= tThetaLow[j] && tanThetaAbs < tThetaHigh[j])  {
+        hPullTheta[j]->Fill(pTPC, pull);
+      }
+    }
+
+    if (!hMap)
+      continue;
+
+    Double_t correctionFactor = 1.;
+    
+    if (isNonPP) {
+      // 1. Correct eta dependence
+      correctionFactor = hMap->GetBinContent(getBinX(hMap, tanTheta), getBinY(hMap, 1./dEdxExpected));
+      
+      // 2. Correct for multiplicity dependence:
+      Double_t multCorrectionFactor = 1.;
+      
+      if (fMultiplicity > 0) {
+        Double_t relSlope = corrFuncMult.Eval(1. / (dEdxExpected * correctionFactor));
+        relSlope += corrFuncMultTanTheta.Eval(tanTheta);
+
+        multCorrectionFactor = 1. + relSlope * fMultiplicity;
+      }
+
+      c1FromSigmaMap = hThetaMapSigmaPar1->GetBinContent(getBinX(hThetaMapSigmaPar1, tanTheta), getBinY(hThetaMapSigmaPar1, 1./dEdxExpected));
+      
+      // Multiplicity dependence of sigma depends on the real dEdx at zero multiplicity, i.e. the eta (only) corrected dEdxExpected value has to be used
+      // since all maps etc. have been created for ~zero multiplicity
+      Double_t relSigmaSlope = corrFuncSigmaMult.Eval(1. / (dEdxExpected * correctionFactor));
+      Double_t multSigmaCorrectionFactor = 1. + relSigmaSlope * fMultiplicity;
+      
+      dEdxExpected *= correctionFactor * multCorrectionFactor; 
+      
+      expectedSigma = dEdxExpected * TMath::Sqrt( c0 * c0 + (c1FromSigmaMap * c1FromSigmaMap) / tpcSignalN);
+      expectedSigma *= multSigmaCorrectionFactor;
+      
+      pull = (dEdx - dEdxExpected) / (plotPull ? expectedSigma: dEdxExpected);
+    }
+    else {
+      correctionFactor = hMap->GetBinContent(getBinX(hMap, tanTheta), getBinY(hMap, 1./dEdxExpected));
+      
+      c1FromSigmaMap = hThetaMapSigmaPar1->GetBinContent(getBinX(hThetaMapSigmaPar1, tanTheta), getBinY(hThetaMapSigmaPar1, 1./dEdxExpected));
+   
+      dEdxExpected *= correctionFactor; // If data is not corrected, but the sigma map is for corrected data, re-do analysis with corrected dEdx
+      
+      expectedSigma = dEdxExpected * TMath::Sqrt( c0 * c0 + (c1FromSigmaMap * c1FromSigmaMap) / tpcSignalN);
+      pull = (dEdx - dEdxExpected) / (plotPull ? expectedSigma: dEdxExpected);
+    }
+
+    pull = (dEdx - dEdxExpected) / (plotPull ? expectedSigma: dEdxExpected);
+
+    hPullAdditionalCorr->Fill(pTPC, pull);
+
+    for (Int_t j = 0; j < nThetaHistos; j++)    {
+      if (tanThetaAbs  >= tThetaLow[j] && tanThetaAbs < tThetaHigh[j])  {
+        hPullAdditionalCorrTheta[j]->Fill(pTPC, pull);
+      }
+    }
+  }
+/*
+  // Mean, Sigma, chi^2/NDF of pull of different theta bins and all in one plot
+  TCanvas* canvPullMean = new TCanvas("canvPullMean", "canvPullMean", 100,10,1380,800);
+  canvPullMean->SetLogx(kTRUE);
+  canvPullMean->SetGridx(kTRUE);
+  canvPullMean->SetGridy(kTRUE);
+  TCanvas* canvPullSigma = new TCanvas("canvPullSigma", "canvPullSigma", 100,10,1380,800);
+  canvPullSigma->SetLogx(kTRUE);
+  canvPullSigma->SetGridx(kTRUE);
+  canvPullSigma->SetGridy(kTRUE);
+  TCanvas* canvPullChi2 = new TCanvas("canvPullChi2", "canvPullChi2", 100,10,1380,800);
+  canvPullChi2->SetLogx(kTRUE);
+  canvPullChi2->SetGridx(kTRUE);
+  canvPullChi2->SetGridy(kTRUE);
+  
+
+  TCanvas* canvPull[nThetaHistos + 1];
+  for (Int_t i = 0, j = nThetaHistos; i < nThetaHistos + 1; i++, j--)  {
+    canvPull[i] = new TCanvas(Form("canvPull_%d", i), "canvPull", 100,10,1380,800);
+    canvPull[i]->cd();
+    canvPull[i]->SetLogx(kTRUE);
+    canvPull[i]->SetLogz(kTRUE);
+    canvPull[i]->SetGrid(kTRUE, kTRUE);
+
+    TH2D* hTemp = 0x0;
+    TString thetaString = "";
+    if (i == nThetaHistos)  {
+      hTemp = hPull;
+      thetaString = "tan(#Theta) integrated";
+    }
+    else {
+      hTemp = hPullTheta[i];
+      thetaString = Form("%.2f #leq |tan(#Theta)| < %.2f", tThetaLow[i], tThetaHigh[i]);
+    }
+    
+    normaliseHisto(hTemp);
+    hTemp->FitSlicesY();
+    hTemp->GetYaxis()->SetNdivisions(12);
+    hTemp->GetXaxis()->SetMoreLogLabels(kTRUE);
+    TH1D* hTempMean = (TH1D*)gDirectory->Get(Form("%s_1", hTemp->GetName()));
+    hTempMean->SetTitle(Form("mean(pull), %s", thetaString.Data()));
+    hTempMean->GetXaxis()->SetMoreLogLabels(kTRUE);
+    hTempMean->SetLineWidth(2);
+    hTempMean->SetMarkerStyle(20);
+    TH1D* hTempSigma = (TH1D*)gDirectory->Get(Form("%s_2", hTemp->GetName()));
+    hTempSigma->SetTitle(Form("#sigma(pull), %s", thetaString.Data()));
+    hTempSigma->GetXaxis()->SetMoreLogLabels(kTRUE);
+    hTempSigma->SetLineColor(kMagenta);
+    hTempSigma->SetMarkerStyle(20);
+    hTempSigma->SetMarkerColor(kMagenta);
+    hTempSigma->SetLineWidth(2);
+    TH1D* hTempChi2 = (TH1D*)gDirectory->Get(Form("%s_chi2", hTemp->GetName()));
+    hTempChi2->SetTitle(Form("#chi^{2} / NDF (pull), %s", thetaString.Data()));
+    hTempChi2->GetXaxis()->SetMoreLogLabels(kTRUE);
+    hTempChi2->SetLineColor(kMagenta + 2);
+    hTempChi2->SetMarkerStyle(20);
+    hTempChi2->SetMarkerColor(kMagenta + 2);
+    hTempChi2->SetLineWidth(2);
+
+    hTemp->DrawCopy("colz");
+    hTempMean->DrawCopy("same");
+    hTempSigma->DrawCopy("same");
+    hTempChi2->Scale(-1./10.);
+    hTempChi2->DrawCopy("same");
+    hTempChi2->Scale(-10.);
+
+    canvPullMean->cd();
+    hTempMean->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempMean->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempMean->DrawCopy((i == 0 ? "" : "same"));
+
+    canvPullSigma->cd();
+    hTempSigma->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempSigma->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempSigma->DrawCopy((i == 0 ? "" : "same"));
+
+    canvPullChi2->cd();
+    hTempChi2->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempChi2->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+    hTempChi2->DrawCopy((i == 0 ? "" : "same"));
+  }
+
+  canvPullMean->BuildLegend();
+  canvPullSigma->BuildLegend();
+  canvPullChi2->BuildLegend();
+*/
+  // Histograms with additional correction
+  TCanvas* canvPullMeanCorr = 0x0;
+  TCanvas* canvPullSigmaCorr = 0x0;
+  TCanvas* canvPullChi2Corr = 0x0;
+  TCanvas* canvPullCorr[nThetaHistos + 1];
+  for (Int_t i = 0; i < nThetaHistos + 1; i++) 
+    canvPullCorr[i] = 0x0;
+  
+  if (hMap) {
+    // Mean, Sigma, chi^2/NDF of pull of different theta bins and all in one plot
+    canvPullMeanCorr = new TCanvas("canvPullMeanCorr", "canvPullMeanCorr", 100,10,1380,800);
+    canvPullMeanCorr->SetLogx(kTRUE);
+    canvPullMeanCorr->SetGridx(kTRUE);
+    canvPullMeanCorr->SetGridy(kTRUE);
+    canvPullSigmaCorr = new TCanvas("canvPullSigmaCorr", "canvPullSigmaCorr", 100,10,1380,800);
+    canvPullSigmaCorr->SetLogx(kTRUE);
+    canvPullSigmaCorr->SetGridx(kTRUE);
+    canvPullSigmaCorr->SetGridy(kTRUE);
+    canvPullChi2Corr = new TCanvas("canvPullChi2Corr", "canvPullChi2Corr", 100,10,1380,800);
+    canvPullChi2Corr->SetLogx(kTRUE);
+    canvPullChi2Corr->SetGridx(kTRUE);
+    canvPullChi2Corr->SetGridy(kTRUE);
+    
+    for (Int_t i = 0, j = nThetaHistos; i < nThetaHistos + 1; i++, j--)  {
+      canvPullCorr[i] = new TCanvas(Form("canvPullCorr_%d", i), "canvPullCorr", 100,10,1380,800);
+      canvPullCorr[i]->cd();
+      canvPullCorr[i]->SetLogx(kTRUE);
+      canvPullCorr[i]->SetLogz(kTRUE);
+      canvPullCorr[i]->SetGrid(kTRUE, kTRUE);
+
+      TH2D* hTemp = 0x0;
+      TString thetaString = "";
+      
+      if (i == nThetaHistos)  {
+        hTemp = hPullAdditionalCorr;
+        thetaString = "tan(#Theta) integrated";
+      }
+      else    {
+        hTemp = hPullAdditionalCorrTheta[i];
+        thetaString = Form("%.2f #leq |tan(#Theta)| < %.2f", tThetaLow[i], tThetaHigh[i]);
+      }
+
+      normaliseHisto(hTemp);
+      hTemp->FitSlicesY();
+      hTemp->GetYaxis()->SetNdivisions(12);
+      hTemp->GetXaxis()->SetMoreLogLabels(kTRUE);
+      TH1D* hTempMean = (TH1D*)gDirectory->Get(Form("%s_1", hTemp->GetName()));
+      hTempMean->SetTitle(Form("mean(pull), %s", thetaString.Data()));
+      hTempMean->GetXaxis()->SetMoreLogLabels(kTRUE);
+      hTempMean->SetLineWidth(2);
+      hTempMean->SetMarkerStyle(20);
+      TH1D* hTempSigma = (TH1D*)gDirectory->Get(Form("%s_2", hTemp->GetName()));
+      hTempSigma->SetTitle(Form("#sigma(pull), %s", thetaString.Data()));
+      hTempSigma->GetXaxis()->SetMoreLogLabels(kTRUE);
+      hTempSigma->SetLineColor(kMagenta);
+      hTempSigma->SetMarkerStyle(20);
+      hTempSigma->SetMarkerColor(kMagenta);
+      hTempSigma->SetLineWidth(2);
+      TH1D* hTempChi2 = (TH1D*)gDirectory->Get(Form("%s_chi2", hTemp->GetName()));
+      hTempChi2->SetTitle(Form("#chi^{2} / NDF (pull), %s", thetaString.Data()));
+      hTempChi2->GetXaxis()->SetMoreLogLabels(kTRUE);
+      hTempChi2->SetLineColor(kMagenta + 2);
+      hTempChi2->SetMarkerStyle(20);
+      hTempChi2->SetMarkerColor(kMagenta + 2);
+      hTempChi2->SetLineWidth(2);
+
+      hTemp->DrawCopy("colz");
+      hTempMean->DrawCopy("same");
+      hTempSigma->DrawCopy("same");
+      hTempChi2->Scale(-1./10.);
+      hTempChi2->DrawCopy("same");
+      hTempChi2->Scale(-10.);
+  
+      canvPullMeanCorr->cd();
+      hTempMean->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempMean->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempMean->DrawCopy((i == 0 ? "" : "same"));
+      
+      canvPullSigmaCorr->cd();
+      hTempSigma->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempSigma->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempSigma->DrawCopy((i == 0 ? "" : "same"));
+      
+      canvPullChi2Corr->cd();
+      hTempChi2->SetLineColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempChi2->SetMarkerColor(1 + ((j >= 9) ? (39 + 2 * (j - 9)) : j));
+      hTempChi2->DrawCopy((i == 0 ? "" : "same"));
+    }
+    
+    canvPullMeanCorr->BuildLegend();
+    canvPullSigmaCorr->BuildLegend();
+    canvPullChi2Corr->BuildLegend();
+  }
+  
+  
+  
+  
+  
+  fSave->cd();
+  /*canvPullMean->Write();
+  canvPullSigma->Write();
+  canvPullChi2->Write();
+  
+  for (Int_t  i = 0; i < nThetaHistos + 1; i++) {
+    canvPull[i]->Write();
+  }*/
+  
+  canvPullMeanCorr->Write();
+  canvPullSigmaCorr->Write();
+  canvPullChi2Corr->Write();
+  
+  for (Int_t  i = 0; i < nThetaHistos + 1; i++) {
+    canvPullCorr[i]->Write();
+  }
+
+  TNamed* info = new TNamed(Form("Theta map: %s\n\nSigma map: %s\n\nSplines file: %s\n\nSplines name: %s", pathNameThetaMap.Data(), 
+                                 pathNameSigmaMap.Data(), pathNameSplinesFile.Data(), prSplinesName.Data()),
+                            "info");
+  info->Write();
+  fSave->Close();
+  
+  return 0;
+}
diff --git a/PWGPP/TPC/macros/PIDCalib/checkShapeEta.C b/PWGPP/TPC/macros/PIDCalib/checkShapeEta.C
new file mode 100644 (file)
index 0000000..3a99ba0
--- /dev/null
@@ -0,0 +1,983 @@
+#include "THnSparse.h"
+#include "TH3D.h"
+#include "TH2D.h"
+#include "TH1D.h"
+#include "TProfile.h"
+#include "TF1.h"
+#include "TFitResultPtr.h"
+#include "TFitResult.h"
+#include "TCanvas.h"
+#include "TStyle.h"
+#include "TVirtualFitter.h"
+#include "TList.h"
+#include "TString.h"
+#include "TLegend.h"
+#include "TLegendEntry.h"
+#include "TFile.h"
+#include "TGraphErrors.h"
+#include "TGraph.h"
+#include "TMath.h"
+#include "TDatime.h"
+
+#include <iostream>
+#include <iomanip>
+
+#include "THnSparseDefinitions.h"
+#include "ProgressBar.h"
+
+Bool_t correctedData = kFALSE;
+
+//--------------------------------------------------
+Double_t getMedianOfNonZeros(Double_t* input, const Int_t dim)
+{
+  Double_t values[dim];
+  for (Int_t i = 0; i < dim; i++)
+    values[i] = 0.0;
+    
+  Int_t numNonZero = 0;
+  
+  for (Int_t i = 0; i < dim; i++) {
+    if (input[i] > 0) {
+      values[numNonZero] = input[i];
+      numNonZero++;
+    }
+  }
+  
+  return ((numNonZero > 0) ? TMath::Median(numNonZero, values) : 0);
+}
+
+
+//--------------------------------------------------
+void prepareHisto(TH2* h)
+{
+  for (Int_t binX = 1; binX <= h->GetXaxis()->GetNbins(); binX++) {
+    
+    // Find maximum for fixed x
+    Double_t maxBinContent = -1;
+    Int_t maxBin = -1;
+    for (Int_t binY = 1; binY <= h->GetYaxis()->GetNbins(); binY++) {
+      Double_t cont = h->GetBinContent(binX, binY);
+      if (cont > maxBinContent) {
+        maxBinContent = cont;
+        maxBin = binY;
+      }
+    }
+    
+    Double_t prevBinContent = -1;
+    Double_t currBinContent = -1;
+    
+    // Start at the maximum, go to the left and remove all bins that fall too steeply (due to TPC cut) by setting large errors
+    for (Int_t binY = maxBin; binY >= 1; binY--) {
+      currBinContent = h->GetBinContent(binX, binY);
+      
+      if (currBinContent > 0)   {
+        Double_t ratio = prevBinContent / currBinContent;
+        
+        if (ratio > 5)    {
+          for (Int_t binY2 = binY; binY2 >= 1; binY2--) {
+            if (h->GetBinContent(binX, binY2) > 0) {
+              h->SetBinContent(binX, binY2, 0);
+              h->SetBinError(binX, binY2, maxBinContent);
+            }
+          }
+          break;
+        }
+      }
+      
+      prevBinContent = currBinContent;
+    }
+    
+    prevBinContent = -1;
+    currBinContent = -1;
+    
+    // Start at the maximum, go to the right and remove all bins that fall too steeply (due to TPC cut) by setting large errors
+    for (Int_t binY = maxBin; binY <= h->GetYaxis()->GetNbins(); binY++) {
+      currBinContent = h->GetBinContent(binX, binY);
+      
+      if (currBinContent > 0)   {
+        Double_t ratio = prevBinContent / currBinContent;
+        
+        if (ratio > 5)    {
+          for (Int_t binY2 = binY; binY2 <= h->GetYaxis()->GetNbins(); binY2++) {
+            if (h->GetBinContent(binX, binY2) > 0) {
+              h->SetBinContent(binX, binY2, 0);
+              h->SetBinError(binX, binY2, maxBinContent);
+            }
+          }
+          break;
+        }
+      }
+      
+      prevBinContent = currBinContent;
+    }    
+  }
+}
+
+
+//--------------------------------------------------
+Bool_t FitHist(TH1 *h, Double_t heightFractionForRange, TString fitOption, Double_t *results, Double_t *resultErrors)
+{
+  if (!h) return kFALSE;
+  if (!results || !resultErrors) return kFALSE;
+  
+  // Average around maximum bin -> Might catch some outliers
+  Int_t maxBin = h->GetMaximumBin();
+  Double_t maxVal = h->GetBinContent(maxBin);
+  
+  if (maxVal < 2) { // It could happen that all entries are in overflow/underflow; don't fit in this case
+    return kFALSE;
+  }
+  
+  UChar_t usedBins = 1;
+  if (maxBin > 1) {
+    maxVal += h->GetBinContent(maxBin - 1);
+    usedBins++;
+  }
+  if (maxBin < h->GetNbinsX()) {
+    maxVal += h->GetBinContent(maxBin + 1);
+    usedBins++;
+  }
+  maxVal /= usedBins;
+  
+  Double_t thresholdFraction = heightFractionForRange * maxVal; 
+  Int_t lowThrBin = TMath::Max(1, h->FindFirstBinAbove(thresholdFraction));
+  Int_t highThrBin = TMath::Min(h->GetNbinsX(), h->FindLastBinAbove(thresholdFraction));
+  
+  Double_t lowThreshold = h->GetBinCenter(lowThrBin);
+  Double_t highThreshold = h->GetBinCenter(highThrBin);
+  
+  TFitResultPtr res = h->Fit("gaus", Form("%sS", fitOption.Data()), "", lowThreshold, highThreshold);
+  
+  if ((Int_t)res == 0) {
+    for (Int_t i = 0; i < 3; i++) {
+      results[i] = res->GetParams()[i];
+      resultErrors[i] = res->GetErrors()[i];
+    }
+    results[3] = res->Ndf()>0 ? res->Chi2()/res->Ndf() : 0;
+    resultErrors[3] = 0;
+    
+    return kTRUE;
+  }
+  
+  return kFALSE;
+}
+
+
+//--------------------------------------------------
+void FitSlicesY(TH2 *hist, Double_t heightFractionForRange, Int_t cutThreshold, TString fitOption, TObjArray *arr)
+{
+  if (!hist) return;
+  if (!arr) return;
+
+  // If old style is to be used
+  /*
+  hist->FitSlicesY(0, 0, -1, cutThreshold, fitOption.Data(), &array);
+  return;
+  */
+  
+  
+  arr->Clear();
+  arr->SetOwner();
+  arr->Expand(4);
+  
+  TAxis *axis=hist->GetXaxis();
+  const TArrayD *bins = axis->GetXbins();
+  TH1D** hList = new TH1D*[4];
+  
+  for (Int_t i = 0; i < 4; i++) {
+    delete gDirectory->FindObject(Form("%s_%d", hist->GetName(), i));
+    
+    if (bins->fN == 0) {
+      hList[i] = new TH1D(Form("%s_%d", hist->GetName(), i), i < 3 ? Form("Fitted value of par[%d]", i) : "Chi2/NDF",
+                          hist->GetNbinsX(), axis->GetXmin(), axis->GetXmax());
+    } else {
+      hList[i] = new TH1D(Form("%s_%d", hist->GetName(), i), i < 3 ? Form("Fitted value of par[%d]", i) : "Chi2/NDF", hist->GetNbinsX(), bins->fArray);
+    }
+    
+    (*arr)[i] = hList[i];
+  }
+  
+  Double_t results[4] = {0.0, 0.0, 0.0, 0.0 };
+  Double_t resultErrors[4] = {0.0, 0.0, 0.0, 0.0 };
+  
+  for (Int_t ibin=0, ibin2=axis->GetFirst(); ibin2<=axis->GetLast(); ++ibin2, ++ibin) {
+    TH1 *h=hist->ProjectionY("_temp",ibin2,ibin2);
+    if (!h)
+      continue;
+    
+    if (h->GetEntries() < cutThreshold) {
+      delete h;
+      continue;
+    }
+    
+    Bool_t fitSuccessful = FitHist(h, heightFractionForRange, fitOption, results, resultErrors);
+    
+    if (fitSuccessful) {
+      Int_t resBin = ibin2;
+      hList[0]->SetBinContent(resBin,results[0]);
+      hList[0]->SetBinError(resBin,resultErrors[0]);
+      hList[1]->SetBinContent(resBin,results[1]);
+      hList[1]->SetBinError(resBin,resultErrors[1]);
+      hList[2]->SetBinContent(resBin,results[2]);
+      hList[2]->SetBinError(resBin,resultErrors[2]);
+      hList[3]->SetBinContent(resBin,results[3]);
+    }
+    
+    delete h;
+  }
+}
+
+
+//--------------------------------------------------
+/*Obsolete: dEdx is NOT corrected!
+TH1D* getSeparation(THnSparse* hist, Bool_t fromV0s, TFile* fSave)
+{
+  TObjArray arr;
+  
+  Int_t binIDel = 1;
+  if (fromV0s)
+    binIDel = 7;
+  
+  Int_t binIDpi = 4;
+  if (fromV0s)
+    binIDpi = 8;
+  
+  //hist->GetAxis(kEta)->SetRangeUser(-0.199, 0.199);
+  
+  // Select and fit electrons
+  hist->GetAxis(kMCpid)->SetRange(binIDel,binIDel); // (V0) electrons
+  hist->GetAxis(kSelectSpecies)->SetRange(1,1); // Delta_electron
+  
+  Int_t referenceAxis = kDeDx;
+  
+  TH2D* histEl = (TH2D*)hist->Projection(referenceAxis, kPtpcInner);
+  histEl->SetName(fromV0s ? "histV0El" : "histEl");
+  histEl->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  histEl->GetYaxis()->SetTitle(hist->GetAxis(referenceAxis)->GetTitle());
+  
+  if (!fromV0s)
+    prepareHisto(histEl);
+  histEl->FitSlicesY(0,0,-1,0,"QNR",&arr); 
+  TH1D* hMeanEl = (TH1D*)arr.At(1)->Clone(fromV0s ? "hMeanV0El" : "hMeanEl");
+  TH1D* hSigmaEl = (TH1D*)arr.At(2)->Clone(fromV0s ? "hSigmaV0El" : "hSigmaEl");
+  TH1D* hChi2El = (TH1D*)arr.At(3)->Clone(fromV0s ? "hChi2V0El" : "hChi2El");
+
+  hMeanEl->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hMeanEl->GetYaxis()->SetTitle("Mean (Gauss)");
+  hSigmaEl->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hSigmaEl->GetYaxis()->SetTitle("#sigma (Gauss)");
+  hChi2El->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hChi2El->GetYaxis()->SetTitle("#chi^{2}/NDF (Gauss)");
+  
+  hMeanEl->SetMarkerStyle(20);
+  hSigmaEl->SetMarkerStyle(20);
+  
+  if (fSave)    {
+    fSave->cd();
+    
+    histEl->Write();
+  }
+  
+  delete histEl;
+
+  // Select and fit pions
+  hist->GetAxis(kMCpid)->SetRange(binIDpi,binIDpi); // (V0) pions
+  hist->GetAxis(kSelectSpecies)->SetRange(3,3); // Delta_pion
+  
+  TH2D* histPi = (TH2D*)hist->Projection(referenceAxis, kPtpcInner);
+  histPi->SetName(fromV0s ? "histV0Pi" : "histPi");
+  histPi->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  histPi->GetYaxis()->SetTitle(hist->GetAxis(referenceAxis)->GetTitle());
+  
+  if (!fromV0s)
+    prepareHisto(histPi);
+  histPi->FitSlicesY(0,0,-1,0,"QNR",&arr);
+  TH1D* hMeanPi = (TH1D*)arr.At(1)->Clone(fromV0s ? "hMeanV0Pi" : "hMeanPi");
+  TH1D* hSigmaPi = (TH1D*)arr.At(2)->Clone(fromV0s ? "hSigmaV0Pi" : "hSigmaPi");
+  TH1D* hChi2Pi = (TH1D*)arr.At(3)->Clone(fromV0s ? "hChi2V0Pi" : "hChi2Pi");
+  
+  hMeanPi->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hMeanPi->GetYaxis()->SetTitle("Mean (Gauss)");
+  hSigmaPi->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hSigmaPi->GetYaxis()->SetTitle("#sigma (Gauss)");
+  hChi2Pi->GetXaxis()->SetTitle(hist->GetAxis(kPtpcInner)->GetTitle());
+  hChi2Pi->GetYaxis()->SetTitle("#chi^{2}/NDF (Gauss)");
+  
+  hMeanPi->SetMarkerStyle(20);
+  hMeanPi->SetLineColor(kRed);
+  hMeanPi->SetMarkerColor(kRed);
+  hSigmaPi->SetMarkerStyle(20);
+  hSigmaPi->SetLineColor(kRed);
+  hSigmaPi->SetMarkerColor(kRed);
+  hChi2Pi->SetMarkerStyle(20);
+  hChi2Pi->SetLineColor(kRed);
+  hChi2Pi->SetMarkerColor(kRed);
+  
+  // Separation
+  
+  TH1D* hSeparation= (TH1D*)hMeanEl->Clone(fromV0s ? "hSeparationV0" : "hSeparation"); //to get same binning
+  hSeparation->SetMarkerStyle(20);
+  hSeparation->GetYaxis()->SetTitle("Separation");
+
+  const Int_t nBins = hMeanEl->GetNbinsX();
+  
+  Double_t deltaMean[nBins] ;
+  Double_t deltaSigma[nBins];
+  
+  for(Int_t i = 0 ;i < nBins; i++) {
+    deltaMean[i] = TMath::Abs(hMeanEl->GetBinContent(i) - hMeanPi->GetBinContent(i));
+    deltaSigma[i] = TMath::Abs((hSigmaEl->GetBinContent(i) + hSigmaPi->GetBinContent(i))) / 2.;
+    
+    if(TMath::Abs(deltaSigma[i]) < 0.000001)
+      continue;
+
+    hSeparation->SetBinContent(i, deltaMean[i] / deltaSigma[i]);
+  }
+
+
+  // Reset ranges
+  hist->GetAxis(kMCpid)->SetRange(0,-1); 
+  hist->GetAxis(kSelectSpecies)->SetRange(0,-1);
+  hist->GetAxis(kEta)->SetRange(0, -1);
+
+  if (fSave)    {
+    fSave->cd();
+
+    histPi->Write();
+    
+    hMeanEl->Write();
+    hMeanPi->Write();
+
+    hSigmaEl->Write();
+    hSigmaPi->Write();
+    
+    hChi2El->Write();
+    hChi2Pi->Write();
+    
+    hSeparation->Write();
+  }
+  
+  delete histPi;
+
+  return hSeparation;
+}
+*/
+
+//--------------------------------------------------
+Int_t checkShapeEta(TString path = ".", Int_t multiplicityStepSize = -1/*-1 for all multiplicities*/,
+                    Int_t maxMultiplicity = 20000,
+                    Int_t onlyEtaShapeComparison = 0, TString fileName = "bhess_PIDetaAdv.root", 
+                    TString listName = "bhess_PIDetaAdv", Int_t momentumAxis = kPtpcInner/* = kPt*/) { 
+                              
+  Double_t binWidthsPt[nPtBins];
+  for (Int_t i = 0; i < nPtBins; i++) 
+    binWidthsPt[i] = (binsPt[i + 1] - binsPt[i]) / 2.;
+
+  Int_t cutForFitting = 10;
+  Double_t heightFractionForFittingRange = 0.1;
+  
+  TList* histList = 0x0;
+  
+       TFile* f = TFile::Open(Form("%s/%s", path.Data(), fileName.Data()));
+  if (!f)  {
+    std::cout << "Failed to open file \"" << Form("%s/%s", path.Data(), fileName.Data()) << "\"!" << std::endl;
+    return -1;
+  }
+  
+  THnSparse* hPIDdata = 0x0;
+  
+  if (correctedData) {
+    hPIDdata = dynamic_cast<THnSparse*>(f->Get("hPIDdataCorrected"));
+    if (!hPIDdata) {
+      std::cout << "Failed to load (extracted) data histo!" << std::endl;
+      return -1;
+    }
+  }
+  else  {
+    histList = (TList*)(f->Get(listName.Data()));
+    if (!histList) {
+      std::cout << "Failed to load list \"" << listName.Data() << "\"!" << std::endl;
+      return -1;
+    }
+    
+    // Extract the data histogram
+    hPIDdata = dynamic_cast<THnSparse*>(histList->FindObject("hPIDdataAll"));
+    if (!hPIDdata) {
+      hPIDdata = dynamic_cast<THnSparse*>(f->Get(Form("%s/hPIDdataAll", listName.Data())));
+      if (!hPIDdata)  {
+        std::cout << "Failed to load data histo!" << std::endl;
+        return -1;
+      }
+    }
+  }
+  
+  // Set proper errors
+  hPIDdata->Sumw2();
+  Long64_t nBinsTHnSparse = hPIDdata->GetNbins();
+  Double_t binContent = 0;
+  
+  for (Long64_t bin = 0; bin < nBinsTHnSparse; bin++) {
+    binContent = hPIDdata->GetBinContent(bin);
+    hPIDdata->SetBinError(bin, TMath::Sqrt(binContent));
+  }
+  
+  // Output file
+  TDatime daTime;
+  TString saveFileName = Form("outputCheckShapeEtaAdv_%04d_%02d_%02d__%02d_%02d.root", daTime.GetYear(), daTime.GetMonth(), 
+                              daTime.GetDay(), daTime.GetHour(), daTime.GetMinute());
+            
+  TFile* fSave = TFile::Open(Form("%s/%s", path.Data(), saveFileName.Data()), "recreate");
+  if (!fSave) {
+    std::cout << "Failed to open save file \"" << Form("%s/%s", path.Data(), saveFileName.Data()) << "\"!" << std::endl;
+    return -1;
+  }
+  
+  // p dependece of eta dependence
+  Bool_t first = kTRUE;
+  
+  Int_t momLow = 1;// = 1 or 7 or 21
+  Int_t momHigh = hPIDdata->GetAxis(momentumAxis)->GetNbins();//;24 or 40 or hPIDdata->GetAxis(momentumAxis)->GetNbins()
+  std::cout << "Momentum range: ";
+  std::cout << hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momLow) << " - " << hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momHigh);
+  std::cout << std::endl;
+  
+  //TF1* constantFunc = new TF1("constantFunc", "1", -2, +2);
+  
+  const Int_t nModes = 3;
+  TString mode[nModes] = {"dE/dx", "Delta_{Species}", "Delta'_{Species}"};
+  Int_t dataAxis[nModes] = {0/*kDeDx*/, 0/*kDelta*/, kDeltaPrime};
+  
+  
+  
+  if (onlyEtaShapeComparison > 0) {
+    // Check, if there is a dependence on the species and/or p directly by choosing momentum bins with equal dE/dx for
+    // different species and plotting Delta vs eta etc.  
+    
+    
+    for (onlyEtaShapeComparison = 1; onlyEtaShapeComparison <= 3; onlyEtaShapeComparison++) { 
+      TCanvas* canvSpecific = new TCanvas(Form("EtaDependenceOfDifferentSpeciesAtSamedEdx_%d", onlyEtaShapeComparison), 
+                                          "#eta dependence of different species at same dE/dx",
+                                          100, 10, 1380 / 2, 700);
+      
+      canvSpecific->SetRightMargin(0.001);
+      canvSpecific->SetTopMargin(0.01);
+      canvSpecific->SetLeftMargin(0.18);
+      canvSpecific->SetBottomMargin(0.11);
+      Int_t speciesColour[6] = { 4, 1, 3, 2, 6, 7 };
+      
+      Int_t momPions = -1;
+      //Int_t momPions2 = hPIDdata->GetAxis(momentumAxis)->FindBin(4.0);
+      //Int_t momPions3 = hPIDdata->GetAxis(momentumAxis)->FindBin(7.0);
+      Int_t momElectrons = -1; 
+      
+      Int_t momProtons = -1;
+      Int_t momKaons = -1;
+      
+      Int_t dEdx = -1;
+      
+      if (onlyEtaShapeComparison == 1)  {// For dE/dx about 50
+        momProtons = hPIDdata->GetAxis(momentumAxis)->FindBin(2.7);
+        momKaons = hPIDdata->GetAxis(momentumAxis)->FindBin(1.3);
+        momPions = hPIDdata->GetAxis(momentumAxis)->FindBin(0.45);
+        dEdx = 50;
+      }
+      else if (onlyEtaShapeComparison == 2)  {// For dE/dx about 75
+        momProtons = hPIDdata->GetAxis(momentumAxis)->FindBin(1.0);
+        momKaons = hPIDdata->GetAxis(momentumAxis)->FindBin(0.5);
+        momElectrons = hPIDdata->GetAxis(momentumAxis)->FindBin(0.6);
+        dEdx = 75;
+      }
+      else if (onlyEtaShapeComparison == 3)  {// For dE/dx about 60
+        momProtons = hPIDdata->GetAxis(momentumAxis)->FindBin(1.4);
+        momKaons = hPIDdata->GetAxis(momentumAxis)->FindBin(0.735);
+        // Sample not clean and V0 pi don't have sufficient statistics momPions = hPIDdata->GetAxis(momentumAxis)->FindBin(3.9);
+        dEdx = 60;
+      }
+      else if (onlyEtaShapeComparison == 4)  {// For dE/dx about 100
+        momProtons = hPIDdata->GetAxis(momentumAxis)->FindBin(0.8);
+        momKaons = hPIDdata->GetAxis(momentumAxis)->FindBin(0.4);
+        dEdx = 100;
+      }
+      else  {
+        std::cout << "Eta shape comparison not defined for this value!" << std::endl;
+        return -1;
+      }
+      
+      
+      /*
+      canvSpecific->Divide(3,1, 0.01, 0.01);
+      
+      Double_t x1, x2, y1, y2;
+      
+      for (Int_t  i = 1; i <= 3; i++) {
+        x1 = 0. + (i-1) * 1./3.;
+        y1 = 0.85;
+        x2 = x1 + 1./3.;
+        y2 = 0.; 
+            
+        canvSpecific->GetPad(i)->SetPad(x1, y1, x2, y2);
+      }*/
+      
+      
+      /*Definitions from ADV task (18.04.14)
+      hist->GetAxis(0)->SetBinLabel(1, "e");
+      hist->GetAxis(0)->SetBinLabel(2, "K");
+      hist->GetAxis(0)->SetBinLabel(3, "#mu");
+      hist->GetAxis(0)->SetBinLabel(4, "#pi");
+      hist->GetAxis(0)->SetBinLabel(5, "p");
+      hist->GetAxis(0)->SetBinLabel(6, "V0+TOF e");
+      hist->GetAxis(0)->SetBinLabel(7, "V0 e");
+      hist->GetAxis(0)->SetBinLabel(8, "V0 #pi");
+      hist->GetAxis(0)->SetBinLabel(9, "V0 p");
+
+      hist->GetAxis(1)->SetTitle("Select Species");
+      hist->GetAxis(1)->SetBinLabel(1, "e");
+      hist->GetAxis(1)->SetBinLabel(2, "K");
+      hist->GetAxis(1)->SetBinLabel(3, "#pi");
+      hist->GetAxis(1)->SetBinLabel(4, "p");
+      */
+      
+      TLegend* legendSpecific = new TLegend(0.62, 0.76, 0.95, 0.95);    
+      //legendSpecific->SetNColumns(2);
+      legendSpecific->SetBorderSize(0);
+      legendSpecific->SetFillColor(0);
+        
+      Bool_t isFirst = kTRUE;
+      
+      Int_t i = 2; //Delta'
+      
+      for (Int_t species = 1; species <= 4/*6*/; species++) {
+        TString histTitle = "";
+        // Select particle species and corresponding deltaSpecies
+        if (species == 1) {// Electrons
+          if (momElectrons < 0)
+            continue;
+          hPIDdata->GetAxis(kMCpid)->SetRange(1,1); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momElectrons, momElectrons);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(1,1); 
+          histTitle = Form("e, %.2f #leq p (GeV/c) #leq %.2f", 
+                            hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momElectrons),
+                            hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momElectrons));
+        }
+        else if (species == 2) {// Kaons
+          hPIDdata->GetAxis(kMCpid)->SetRange(2,2); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momKaons, momKaons);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(2,2); 
+          histTitle = Form("K, %.2f #leq p (GeV/c) #leq %.2f", 
+                            hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momKaons),
+                            hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momKaons));
+        }
+        else if (species == 3) {// Pions
+          if (momPions < 0)
+            continue;
+          hPIDdata->GetAxis(kMCpid)->SetRange(4,4); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momPions, momPions);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(3,3);
+          histTitle = Form("#pi, %.2f #leq p (GeV/c) #leq %.2f", 
+                            hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momPions),
+                            hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momPions));
+        }
+        else if (species == 4) {// Protons
+          hPIDdata->GetAxis(kMCpid)->SetRange(5,5); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momProtons, momProtons);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(4,4);
+          histTitle = Form("p, %.2f #leq p (GeV/c) #leq %.2f", 
+                            hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momProtons),
+                            hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momProtons));
+        }/*
+        else if (species == 5) {// Pions at higher momentum
+          hPIDdata->GetAxis(kMCpid)->SetRange(4,4); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momPions2, momPions2);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(3,3);
+        }
+        else if (species == 6) {// Pions at even higher momentum
+          hPIDdata->GetAxis(kMCpid)->SetRange(4,4); // Only particles of species X
+          hPIDdata->GetAxis(momentumAxis)->SetRange(momPions3, momPions3);
+          hPIDdata->GetAxis(kSelectSpecies)->SetRange(3,3);
+        }*/
+        
+        
+        TH2D* hEta = hPIDdata->Projection(dataAxis[i], kEta);
+        hEta->Rebin2D(2, 1);
+        hEta->SetName(Form("hEta_%s_%d_%d", mode[i].Data(), onlyEtaShapeComparison, species));
+        hEta->SetTitle(Form("Eta dependence of %s for %s", mode[i].Data(),  hPIDdata->GetAxis(kMCpid)->GetBinLabel(species)));
+      
+        TObjArray aSlices;
+        FitSlicesY(hEta, heightFractionForFittingRange, cutForFitting, "QNR", &aSlices);
+        TH1D* hEtaProj = (TH1D*)(aSlices.At(1)->Clone(Form("hEtaProj_%s_%d_%d", mode[i].Data(), onlyEtaShapeComparison, species)));
+        hEtaProj->SetMarkerStyle(20 + species - 1);
+        hEtaProj->SetTitle(histTitle.Data());
+        hEtaProj->SetLineColor(speciesColour[species - 1]);
+        hEtaProj->SetMarkerColor(speciesColour[species - 1]);
+        
+        hEtaProj->GetXaxis()->SetTitle(hPIDdata->GetAxis(kEta)->GetTitle());
+        
+        // Scale to unity
+        Double_t integral = 0;
+        Double_t unityIntegral = 0;
+        for (Int_t j = 1; j <= hEtaProj->GetNbinsX(); j++) {
+          if (hEtaProj->GetBinContent(j) > 0) {
+            // Remove bins with too large error
+            if (hEtaProj->GetBinError(j) > 0.005) {
+              hEtaProj->SetBinContent(j, -1);
+              continue;
+            }
+            
+            unityIntegral += 1;//hEtaProj->GetXaxis()->GetBinWidth(j);
+            integral += hEtaProj->GetBinContent(j);
+          }
+        }
+        hEtaProj->Scale(unityIntegral/integral);
+        
+        hEtaProj->SetStats(kFALSE);
+        hEtaProj->GetYaxis()->SetRangeUser(0.97,1.059);
+        hEtaProj->GetYaxis()->SetTitle("#Delta'_{#lower[-0.5]{scaled}}");
+        hEtaProj->GetYaxis()->SetTitleOffset(1.4);
+        
+        hEtaProj->DrawCopy(Form("%s", (isFirst ? "" : "same")));
+        isFirst = kFALSE;
+        
+        legendSpecific->AddEntry(hEtaProj, hEtaProj->GetTitle(), "flp");
+        
+        //hEtaProj->Fit("pol3", "+", "same", -1, 0);
+        //hEtaProj->Fit("pol3", "+", "same", 0, 1);
+        //((TF1*)hEtaProj->GetListOfFunctions()->At(0))->SetLineColor(species);
+        //((TF1*)hEtaProj->GetListOfFunctions()->At(1))->SetLineColor(species);
+      }
+      
+      legendSpecific->Draw();
+      
+      ClearTitleFromHistoInCanvas(canvSpecific);
+      
+      //canvSpecific->cd(0);
+      /*
+      TLegend* legendSpecific = new TLegend(0.1, 0.85, 0.9, 0.99);    
+      legendSpecific->SetNColumns(2);
+      legendSpecific->SetBorderSize(0);
+      legendSpecific->SetFillColor(0);
+      
+      
+      TLegendEntry* ent = legendSpecific->AddEntry((TObject*)0, path.Data(), "");
+      ent->SetTextColor(1);
+      
+      ent = legendSpecific->AddEntry((TObject*)0, "", "");
+      
+      ent = legendSpecific->AddEntry((TObject*)0, Form("K, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momKaons),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momKaons)),
+                                    "p");
+      ent->SetLineColor(speciesColour[1]);
+      ent->SetTextColor(speciesColour[1]);
+      ent->SetMarkerColor(speciesColour[1]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+      
+      ent = legendSpecific->AddEntry((TObject*)0, Form("e, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momElectrons),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momElectrons)),
+                                    "p");
+      ent->SetLineColor(speciesColour[0]);
+      ent->SetTextColor(speciesColour[0]);
+      ent->SetMarkerColor(speciesColour[0]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+        
+      ent = legendSpecific->AddEntry((TObject*)0, Form("p, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momProtons),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momProtons)),
+                                    "p");
+      ent->SetLineColor(speciesColour[3]);
+      ent->SetTextColor(speciesColour[3]);
+      ent->SetMarkerColor(speciesColour[3]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+      
+      ent = legendSpecific->AddEntry((TObject*)0, Form("#pi, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momPions),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momPions)),
+                                    "p");
+      ent->SetLineColor(speciesColour[2]);
+      ent->SetTextColor(speciesColour[2]);
+      ent->SetMarkerColor(speciesColour[2]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+      
+      ent = legendSpecific->AddEntry((TObject*)0, Form("V0 #pi, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momPions2),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momPions2)),
+                                    "p");
+      ent->SetLineColor(speciesColour[4]);
+      ent->SetTextColor(speciesColour[4]);
+      ent->SetMarkerColor(speciesColour[4]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+      
+      ent = legendSpecific->AddEntry((TObject*)0, Form("V0 #pi, %.2f #leq p (GeV/c) #leq %.2f", 
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinLowEdge(momPions3),
+                                                      hPIDdata->GetAxis(momentumAxis)->GetBinUpEdge(momPions3)),
+                                    "p");
+      ent->SetLineColor(speciesColour[5]);
+      ent->SetTextColor(speciesColour[5]);
+      ent->SetMarkerColor(speciesColour[5]);
+      ent->SetMarkerStyle(2);
+      ent->SetMarkerSize(2);
+        
+      legendSpecific->Draw("");
+      */
+      fSave->cd();
+      canvSpecific->Write();
+      TString pdfName = saveFileName;
+      pdfName = pdfName.ReplaceAll(".root", Form("__dEdx_%d.pdf", dEdx));
+      canvSpecific->SaveAs(Form("%s/%s", path.Data(), pdfName.Data()));
+    }
+    
+    return 0;
+  }
+  
+  // Reset ranges
+  hPIDdata->GetAxis(kMCpid)->SetRange(0, -1);
+  hPIDdata->GetAxis(momentumAxis)->SetRange(0, -1);
+  hPIDdata->GetAxis(kSelectSpecies)->SetRange(0, -1);
+  
+  
+  
+  
+  
+  
+  /*
+  getSeparation(hPIDdata, kFALSE, fSave);
+  getSeparation(hPIDdata, kTRUE, fSave);
+  return 0;*/
+  
+  // If V0s are available in histo, add them! Also: Backward compatibility to data type with "all id. primaries" bin instead of "V0plusTOF el"
+  Int_t additionalV0bins = 0;
+  
+  Bool_t oldStyleWithAllIDprimaries = strcmp(hPIDdata->GetAxis(kMCpid)->GetBinLabel(6), "all id. primaries") == 0;
+  printf("Backward compatibility: Checking bin label of bin 6 of MCpid axis: %s -> %s\n",
+         hPIDdata->GetAxis(kMCpid)->GetBinLabel(6), oldStyleWithAllIDprimaries ? "Old style" : "New style");
+  
+  if (hPIDdata->GetAxis(kMCpid)->GetNbins() >= 8)
+    additionalV0bins = oldStyleWithAllIDprimaries ? 3 : 4;
+  
+  // If valid multiplicityStepSize, fill each multiplicity bin + 1 bin with all multiplicities. Otherwise: Only bin with all multiplicities
+  const Int_t nMultBins = (multiplicityStepSize <= 0) ? 1 :
+                          (((Int_t)((Double_t)maxMultiplicity / multiplicityStepSize)) + ((maxMultiplicity % multiplicityStepSize) != 0) + 1);
+  
+  const Int_t nSpeciesBins = (oldStyleWithAllIDprimaries ? 6 : 5) + additionalV0bins;
+  TH2D* hPEtaDependence[nMultBins][nSpeciesBins];
+  
+  TH1D* hPMeandEdxDependence[nMultBins][nSpeciesBins];
+  
+  const Int_t numTotalBins = (momHigh - momLow + 1) * (4 + additionalV0bins) * nMultBins; // nMomBins * nSpecies * nMultBins
+  
+  for (Int_t i = 0; i < nMultBins; i++) {
+    TString currDir = (i == nMultBins - 1) ? "AllMultiplicites" : 
+                      Form("Multiplicity%d_%d", i * multiplicityStepSize, TMath::Min(maxMultiplicity, (i + 1) * multiplicityStepSize));
+    
+    TString multTitle = (i == nMultBins - 1) ? "all multiplicites" :
+                        Form("multiplicity %d - %d", i * multiplicityStepSize, TMath::Min(maxMultiplicity, (i + 1) * multiplicityStepSize));
+                        
+    fSave->cd();
+    fSave->mkdir(currDir.Data());
+    // nSpeciesBins + 1 to take into account bin "all primaries"
+    for (Int_t species = 1, deltaSpecies = 1; species < nSpeciesBins + 1; species++, deltaSpecies++) {
+      if (species == 3) {
+        deltaSpecies--;
+        continue; // skip muons
+      }
+      else if (species == 6)    {
+        if (oldStyleWithAllIDprimaries) {
+          continue; // skip bin "all primaries"
+        }
+        else {
+          deltaSpecies = 1; // V0+TOF el
+        }
+      }
+      else if (species == 7)    {
+        deltaSpecies = 1; // V0 el
+      }
+      else if (species == 8)    {
+        deltaSpecies = 3; // V0 pi
+      }
+      else if (species == 9)    {
+        deltaSpecies = 4; // V0 pr
+      }
+      
+      first = kTRUE;
+      
+      hPMeandEdxDependence[i][species - 1] = new TH1D(Form("hPMeandEdxDependenceDeltaPrime_%s_%s", partShortName[species - 1].Data(), currDir.Data()), 
+                                                      Form("Momentum dependence of mean dEdx (Delta'_{Species}) of %s, %s",
+                                                           partShortName[species - 1].Data(), multTitle.Data()),
+                                                           nPtBins, binsPt); 
+      
+      hPMeandEdxDependence[i][species - 1]->GetXaxis()->SetTitle(hPIDdata->GetAxis(momentumAxis)->GetTitle());
+      hPMeandEdxDependence[i][species - 1]->GetYaxis()->SetTitle("#Delta'");
+      
+      
+      
+      
+      hPEtaDependence[i][species - 1] = new TH2D(Form("hPEtaDependence_species_DeltaPrime_%s_%s", partShortName[species - 1].Data(), currDir.Data()), 
+                                                 Form("Momentum dependence of eta dependence (#Delta'_{Species}) of %s, %s",
+                                                      partShortName[species - 1].Data(), multTitle.Data()),
+                                                 nPtBins, binsPt, 
+                                                 hPIDdata->GetAxis(kEta)->GetNbins(), hPIDdata->GetAxis(kEta)->GetBinLowEdge(1),
+                                                 hPIDdata->GetAxis(kEta)->GetBinUpEdge(hPIDdata->GetAxis(kEta)->GetNbins())); 
+      
+      hPEtaDependence[i][species - 1]->GetXaxis()->SetTitle(hPIDdata->GetAxis(momentumAxis)->GetTitle());
+      hPEtaDependence[i][species - 1]->GetYaxis()->SetTitle(hPIDdata->GetAxis(kEta)->GetTitle());
+      hPEtaDependence[i][species - 1]->GetYaxis()->SetLabelSize(0.03);
+      hPEtaDependence[i][species - 1]->GetYaxis()->SetTitleOffset(0.9);
+      hPEtaDependence[i][species - 1]->GetXaxis()->SetTitleOffset(1.2);
+      hPEtaDependence[i][species - 1]->GetZaxis()->SetTitle("#Delta'");
+      hPEtaDependence[i][species - 1]->GetZaxis()->SetTitleOffset(0.8);
+      
+      TH2D* hPEtaDependenceScaled = (TH2D*)hPEtaDependence[i][species - 1]->Clone(Form("%s_scaled", hPEtaDependence[i][species - 1]->GetName())); 
+      hPEtaDependenceScaled->SetTitle(Form("%s (scaled)", hPEtaDependenceScaled->GetTitle()));
+      
+      hPIDdata->GetAxis(kMCpid)->SetRange(species,species); // Only particles of type X
+      hPIDdata->GetAxis(kSelectSpecies)->SetRange(deltaSpecies,deltaSpecies); // Seclect corresponding deltaSpecies
+      // Seclect corresponding multiplicity range (last bin (or the only bin) = all multiplicities)
+      if (i == nMultBins - 1)
+        hPIDdata->GetAxis(kMultiplicity)->SetRange(0, -1); // All multiplicities
+      else
+        hPIDdata->GetAxis(kMultiplicity)->SetRangeUser(i * multiplicityStepSize, TMath::Min(maxMultiplicity, (i + 1) * multiplicityStepSize)); 
+      
+      hPIDdata->GetAxis(momentumAxis)->SetRange(0, -1); // Full momentum range
+      TH3D* h3Dproj = hPIDdata->Projection(momentumAxis, kDeltaPrime, kEta);
+      
+      for (Int_t momentum_Bin = momLow; momentum_Bin <= momHigh; momentum_Bin++)  {
+        h3Dproj->GetXaxis()->SetRange(momentum_Bin, momentum_Bin); // Select p/pT bin
+        
+        TH1D* hMeandEdx = (TH1D*)h3Dproj->Project3D("y");
+        hMeandEdx->SetName(Form("hMeandEdx_DeltaPrime_%s_%d_%d", partShortName[species - 1].Data(), momentum_Bin, i));
+        hMeandEdx->SetTitle(Form("p dependence of mean dEdx of #Delta'_{Species} for %s (p %.3f), %s",
+                            hPIDdata->GetAxis(kMCpid)->GetBinLabel(species), hPIDdata->GetAxis(momentumAxis)->GetBinCenter(momentum_Bin),
+                            multTitle.Data()));
+        
+        Double_t results[4] = {0.0, 0.0, 0.0, 0.0 };
+        Double_t resultErrors[4] = {0.0, 0.0, 0.0, 0.0 };
+  
+        Bool_t meandEdxFitSuccessful = FitHist(hMeandEdx, heightFractionForFittingRange, "QN", results, resultErrors);
+        if (meandEdxFitSuccessful) {
+          hPMeandEdxDependence[i][species - 1]->SetBinContent(momentum_Bin, results[1]);
+          hPMeandEdxDependence[i][species - 1]->SetBinError(momentum_Bin, resultErrors[1]);
+        }
+        
+        TH2D* hEta = (TH2D*)h3Dproj->Project3D("yz");
+        hEta->SetName(Form("hEta_DeltaPrime_%d_%d_%d", species, momentum_Bin, i));
+        
+        hEta->SetTitle(Form("p dependence of eta dependence of #Delta' for %s (p %.3f), %s",
+                            hPIDdata->GetAxis(kMCpid)->GetBinLabel(species), hPIDdata->GetAxis(momentumAxis)->GetBinCenter(momentum_Bin),
+                            multTitle.Data()));
+                    
+        TObjArray aSlices;
+        FitSlicesY(hEta, heightFractionForFittingRange, cutForFitting, "QN", &aSlices);
+        
+        TH1D* hEtaProj = (TH1D*)(aSlices.At(1)->Clone(Form("hEtaProj_DeltaPrime_%s_p_%.3f", partShortName[species - 1].Data(), 
+                                                           hPIDdata->GetAxis(momentumAxis)->GetBinCenter(momentum_Bin))));
+        
+        if (!hEtaProj)  {
+          std::cout << "Failed to load eta projection from FitSlicesY!" << std::endl;
+          return -1;
+        }
+        
+        first = kFALSE;
+        
+        for (Int_t etaBin = 1; etaBin <= hEtaProj->GetXaxis()->GetNbins(); etaBin++)  {
+          hPEtaDependence[i][species - 1]->SetBinContent(momentum_Bin, etaBin, hEtaProj->GetBinContent(etaBin));
+          hPEtaDependence[i][species - 1]->SetBinError(momentum_Bin, etaBin, hEtaProj->GetBinError(etaBin));
+        }
+                
+        Int_t nBinsFinishedMomentum = (momentum_Bin - momLow + 1);
+        Int_t numSpecies = species;
+        if (species > 3)
+          numSpecies--; // Muons not used
+          
+        if (oldStyleWithAllIDprimaries && species > 6)
+          numSpecies--; // All primaries not used
+        Int_t nBinsFinishedSpecies = (numSpecies - 1) * (momHigh - momLow + 1);
+        Int_t nBinsFinishedMult = i * (4 + additionalV0bins) * (momHigh - momLow + 1);
+        
+        delete hMeandEdx;
+        delete hEta;
+        delete hEtaProj;
+        
+        progressbar(100. * ( ((Double_t) (nBinsFinishedMomentum + nBinsFinishedSpecies + nBinsFinishedMult)) / numTotalBins));
+        
+      }
+      
+      delete h3Dproj;
+      
+      hPEtaDependence[i][species - 1]->GetXaxis()->SetRangeUser(0.15, 5.0);
+      hPEtaDependence[i][species - 1]->GetZaxis()->SetRangeUser(0.9, 1.1);
+      
+      fSave->cd(currDir.Data());
+      if (hPEtaDependence[i][species - 1]) {
+        hPEtaDependence[i][species - 1]->SetObjectStat(0);
+        hPEtaDependence[i][species - 1]->Write();
+      }
+      
+      if (hPMeandEdxDependence[i][species - 1]) {
+        hPMeandEdxDependence[i][species - 1]->GetXaxis()->SetRangeUser(0.15, 5.0);
+        hPMeandEdxDependence[i][species - 1]->GetYaxis()->SetRangeUser(0.9, 1.1);
+        hPMeandEdxDependence[i][species - 1]->Write();
+      }
+      
+      // Scale histograms 
+      const Int_t nEtaBins = hPEtaDependence[i][species - 1]->GetYaxis()->GetNbins();
+                              
+      for (Int_t momentum_Bin = momLow; momentum_Bin <= momHigh; momentum_Bin++)  {
+                
+        // Scale such that radially emitted particles have deltaPrime equal 1!!
+        // To be robust against fluctuations: Take median of a few eta bins around eta=0 for scaling
+        Double_t values[nEtaBins];
+        for (Int_t etaBin = 1; etaBin <= nEtaBins; etaBin++)
+          values[etaBin - 1] = 0;
+          
+        for (Int_t etaBin = hPEtaDependence[i][species - 1]->GetYaxis()->FindBin(-0.175); 
+                  etaBin <= hPEtaDependence[i][species - 1]->GetYaxis()->FindBin(+0.175); etaBin++)  {
+          Double_t temp = hPEtaDependence[i][species - 1]->GetBinContent(momentum_Bin, etaBin);
+          values[etaBin - 1] = (temp > 0) ? temp : 0;
+        }
+        
+        Double_t temp = getMedianOfNonZeros(values, nEtaBins);
+        
+        if (temp <= 0) 
+          continue;
+        
+        Double_t scale = 1. / temp;
+        
+        for (Int_t etaBin = 1; etaBin <= nEtaBins; etaBin++)  {
+          hPEtaDependenceScaled->SetBinContent(momentum_Bin, etaBin, 
+                                                scale * hPEtaDependence[i][species - 1]->GetBinContent(momentum_Bin, etaBin));
+          hPEtaDependenceScaled->SetBinError(momentum_Bin, etaBin, 
+                                              scale * hPEtaDependence[i][species - 1]->GetBinError(momentum_Bin, etaBin));
+        }
+      }
+      
+      hPEtaDependenceScaled->GetXaxis()->SetRangeUser(0.15, 5.0);
+      fSave->cd(currDir.Data());
+      hPEtaDependenceScaled->SetDrawOption("surf1");
+      hPEtaDependenceScaled->Write();
+      
+      delete hPEtaDependenceScaled;
+    }
+  }
+  
+  // Reset ranges
+  hPIDdata->GetAxis(kMCpid)->SetRange(0, -1);
+  hPIDdata->GetAxis(momentumAxis)->SetRange(0, -1);
+  hPIDdata->GetAxis(kSelectSpecies)->SetRange(0, -1);
+  hPIDdata->GetAxis(kMultiplicity)->SetRange(0, -1);
+  
+  progressbar(100.);
+  printf("\n");
+
+  /*
+  printf("Determining separation...\n");
+
+  getSeparation(hPIDdata, kFALSE, fSave);
+  getSeparation(hPIDdata, kTRUE, fSave);
+  */
+  return 0; 
+}
diff --git a/PWGPP/TPC/macros/PIDCalib/checkShapeEtaTree.C b/PWGPP/TPC/macros/PIDCalib/checkShapeEtaTree.C
new file mode 100644 (file)
index 0000000..bd27e2d
--- /dev/null
@@ -0,0 +1,2139 @@
+#include "TTree.h"
+#include "TH2D.h"
+#include "TH1D.h"
+#include "TH3D.h"
+#include "TH3F.h"
+#include "THnSparse.h"
+#include "TProfile.h"
+#include "TProfile2D.h"
+#include "TF1.h"
+#include "TFitResultPtr.h"
+#include "TFitResult.h"
+#include "TCanvas.h"
+#include "TStyle.h"
+#include "TVirtualFitter.h"
+#include "TLinearFitter.h"
+#include "TList.h"
+#include "TString.h"
+#include "TLegend.h"
+#include "TLegendEntry.h"
+#include "TFile.h"
+#include "TGraphErrors.h"
+#include "TGraph.h"
+#include "TMath.h"
+#include "TDatime.h"
+#include "TSystem.h"
+#include "TSpline.h"
+
+#include "AliPID.h"
+
+#include <iostream>
+#include <iomanip>
+
+#include "THnSparseDefinitions.h"
+#include "ProgressBar.h"
+
+enum mapType {
+  kNoNormalisation = 1,
+  kSmallEtaNormalisation = 2,
+  kLargeEtaNormalisation = 3
+};
+enum type {
+  kTypeDelta = 1,
+  kTypeDeltaPrime = 2,
+  kTypeSigmaDeltaPrime = 3
+};
+
+const Double_t binContentThreshold = 1e-4;
+const Double_t massProton = AliPID::ParticleMass(AliPID::kProton);
+const Double_t massPion = AliPID::ParticleMass(AliPID::kPion);
+const TString suffixMapType[kLargeEtaNormalisation + 1] = {"", "NoNormalisation", "SmallEtaNormalisation", "LargeEtaNormalisation"};
+
+TF1 fGauss("fGauss", "[0]*TMath::Gaus(x, [1], [2], 1)", 0.6, 1.6);
+
+
+//TODO NOW getBin... functions needed? Only, if correction (momentum) necessary
+//_________________________________________________________________________________________
+Int_t getBinX(TH2D* h, Double_t tanTheta)
+{
+  Int_t binX = h->GetXaxis()->FindBin(tanTheta);
+  
+  if (binX <= 0)
+    binX = 1;
+  if (binX > h->GetXaxis()->GetNbins())
+    binX = h->GetXaxis()->GetNbins();
+
+    return binX;
+}
+
+
+//_________________________________________________________________________________________
+Int_t getBinY(TH2D* h, Double_t dEdxInv)
+{
+  Int_t binY = h->GetYaxis()->FindBin(dEdxInv);
+  
+  if (binY <= 0)
+    binY = 1;
+  if (binY > h->GetYaxis()->GetNbins())
+    binY = h->GetYaxis()->GetNbins();
+
+  return binY;
+}
+
+
+//__________________________________________________________________________________________
+Bool_t FindFitRange(TH1* h, Double_t& lowThreshold, Double_t& highThreshold, Double_t fractionForRange = 0.1)
+{
+    lowThreshold = 0.6;
+    highThreshold = 1.6;
+    
+    if (!h)
+      return kFALSE;
+    
+    // Average around maximum bin -> Might catch some outliers
+    Int_t maxBin = h->GetMaximumBin();
+    Double_t maxVal = h->GetBinContent(maxBin);
+    UChar_t usedBins = 1;
+    if (maxBin > 1) {
+      maxVal += h->GetBinContent(maxBin - 1);
+      usedBins++;
+    }
+    if (maxBin < h->GetNbinsX()) {
+      maxVal += h->GetBinContent(maxBin + 1);
+      usedBins++;
+    }
+    maxVal /= usedBins;
+    
+    Double_t thresholdFraction = fractionForRange * maxVal; 
+    Int_t lowThrBin = TMath::Max(1, h->FindFirstBinAbove(thresholdFraction));
+    Int_t highThrBin = TMath::Min(h->GetNbinsX(), h->FindLastBinAbove(thresholdFraction));
+    
+    lowThreshold = h->GetBinCenter(lowThrBin);
+    highThreshold = h->GetBinCenter(highThrBin);
+    
+    return kTRUE;
+}
+
+
+//__________________________________________________________________________________________
+void SetHistAxis(TH2D* h, Int_t type)
+{
+  h->GetXaxis()->SetTitle("tan(#Theta)");
+  h->GetYaxis()->SetTitle("1/(dE/dx) (arb. unit)");
+  if (type == kTypeDelta)
+    h->GetZaxis()->SetTitle("#Delta (arb. unit)");
+  else if (type == kTypeDeltaPrime) 
+    h->GetZaxis()->SetTitle("#Delta' (arb. unit)");
+  else if (type == kTypeSigmaDeltaPrime) 
+    h->GetZaxis()->SetTitle("Par1(#sigma_{rel}(#Delta'))");
+  else
+    printf("SetHistAxis: Unknown type %d!\n", type);
+  h->GetXaxis()->SetTitleSize(0.04);
+  h->GetXaxis()->SetTitleOffset(2.2);
+  h->GetXaxis()->SetLabelSize(0.03);
+  h->GetYaxis()->SetTitleSize(0.04);
+  h->GetYaxis()->SetTitleOffset(2.6);
+  h->GetYaxis()->SetLabelSize(0.03);
+  h->GetZaxis()->SetTitleSize(0.04);
+  h->GetZaxis()->SetTitleOffset(1.3);
+}
+
+//__________________________________________________________________________________________
+Double_t getMedianOfNonZeros(Double_t* input, const Int_t dim)
+{
+  Double_t values[dim];
+  for (Int_t i = 0; i < dim; i++)
+    values[i] = 0.0;
+    
+  Int_t numNonZero = 0;
+  
+  for (Int_t i = 0; i < dim; i++) {
+    if (input[i] > 0) {
+      values[numNonZero] = input[i];
+      numNonZero++;
+    }
+  }
+
+  return ((numNonZero > 0) ? TMath::Median(numNonZero, values) : 0);
+}
+
+
+//__________________________________________________________________________________________
+void normaliseHisto(TH2D* h, Double_t lowerLimit, Double_t upperLimit, Int_t type)
+{
+  if (lowerLimit >= upperLimit) {
+    printf("!!!Error normaliseHisto: lowerLimit >= upperLimit!\n");
+    return;
+  }
+  
+  if (lowerLimit < 0 || upperLimit < 0) {
+    printf("!!!Error normaliseHisto: lowerLimit, upperLimit >= 0 required!\n");
+    return;
+  }
+  
+  Int_t binLow = h->GetXaxis()->FindBin(lowerLimit);
+  if (binLow < 1)
+    binLow = 1;
+  
+  Int_t binHigh = h->GetXaxis()->FindBin(upperLimit);
+  if (binHigh > h->GetXaxis()->GetNbins())
+    binHigh = h->GetXaxis()->GetNbins();
+  
+  Int_t binLow2 = h->GetXaxis()->FindBin(-upperLimit);
+  if (binLow2 < 1)
+    binLow2 = 1;
+  
+  Int_t binHigh2 = h->GetXaxis()->FindBin(-lowerLimit);
+  if (binHigh2 > h->GetXaxis()->GetNbins())
+    binHigh2 = h->GetXaxis()->GetNbins();
+  
+  // If 0 is included in range, it might happen that both ranges overlap -> Remove one of the double counted binsPforMap
+    if (binHigh2 >= binLow) {
+      binHigh2 = binLow - 1;
+      if (binHigh2 < 1)   {
+        printf("!!!Error: binHigh2 = binLow - 1 is < 1\n");
+        return;
+      }
+    }
+    
+    if (binLow2 > binHigh2)
+      binLow2 = binHigh2;
+    
+    // Normalise with respect to some given tan(theta)
+      // To be robust against fluctuations: Take median of a few tan(theta) bins around the desired value for scaling
+      const Int_t nThetaBins = (binHigh - binLow + 1) + (binHigh2 - binLow2 + 1);
+      
+      if (nThetaBins <= 0)  {
+        printf("Cannot renormalise histo due to bad limits for reference bins: %f -> %f\n", lowerLimit, upperLimit);
+        return;
+      }
+      
+      Double_t* values;
+      values = new Double_t[nThetaBins];
+      
+      for (Int_t i = 1; i <= h->GetYaxis()->GetNbins(); i++) {
+        // Reset values
+        Int_t binIndex = 0;
+        for (Int_t thetaBin = 1; thetaBin <= nThetaBins; thetaBin++)
+          values[thetaBin - 1] = 0;
+        
+        for (Int_t thetaBin = binLow; thetaBin <= binHigh; thetaBin++)  {
+          Double_t temp = h->GetBinContent(thetaBin, i);
+          values[binIndex] = (temp > 0 || type == kTypeDelta) ? temp : 0;
+          binIndex++;
+        }
+        for (Int_t thetaBin = binLow2; thetaBin <= binHigh2; thetaBin++)  {
+          Double_t temp = h->GetBinContent(thetaBin, i);
+          values[binIndex] = (temp > 0 || type == kTypeDelta) ? temp : 0;
+          binIndex++;
+        }
+        
+        Double_t temp = 0;
+        Double_t scale = 0;
+        
+        if (type == kTypeDeltaPrime)  {
+          temp = getMedianOfNonZeros(values, nThetaBins);
+          if (temp <= 0)
+            continue;
+          scale = 1. / temp;
+        }
+        else if (type == kTypeDelta)  {
+          scale = TMath::Median(nThetaBins, values);
+        }
+        else  {
+          printf("Error: Type %d not supported for normalisation!\n", type);
+          return;
+        }
+        
+        
+        for (Int_t thetaBin = 1; thetaBin <= h->GetXaxis()->GetNbins(); thetaBin++)  {
+          if (type == kTypeDeltaPrime)  {
+            h->SetBinContent(thetaBin, i, h->GetBinContent(thetaBin, i) * scale);
+            h->SetBinError(thetaBin, i, h->GetBinError(thetaBin, i) * scale);
+          }
+          else if (type == kTypeDelta)  {
+            h->SetBinContent(thetaBin, i, h->GetBinContent(thetaBin, i) - scale);
+            h->SetBinError(thetaBin, i, h->GetBinError(thetaBin, i) - scale);
+          }
+        }
+      }  
+      delete values;
+}
+
+
+//__________________________________________________________________________________________
+void eliminateNonPositivePointsInHisto(TH2D* h)
+{
+  const Int_t nNeighbours = 24;
+  Double_t* values;
+  values = new Double_t[nNeighbours];
+
+  // Search for bins with content <= 0 (bins with fit errors). Then take all surrounding points in +-2 rows and columns
+  // and take their median (only those bins without fit errors!) as the new bin content of the considered bin.
+  
+  for (Int_t binX = 1; binX <= h->GetXaxis()->GetNbins(); binX++) {
+    Int_t firstBinLeft = TMath::Max(1, binX - 2);
+    Int_t lastBinRight = TMath::Min(h->GetXaxis()->GetNbins(), binX + 2);
+    
+    for (Int_t binY = 1; binY <= h->GetYaxis()->GetNbins(); binY++) {
+      if (h->GetBinContent(binX, binY) <= 0) {
+        Int_t firstBinBelow = TMath::Max(1, binY - 2);
+        Int_t lastBinAbove = TMath::Min(h->GetYaxis()->GetNbins(), binY + 2);
+        
+        // Reset values
+        Int_t binIndex = 0;
+        for (Int_t i = 0; i < nNeighbours; i++)
+          values[i] = 0;
+        
+        for (Int_t binX2 = firstBinLeft; binX2 <= lastBinRight; binX2++) {
+          for (Int_t binY2 = firstBinBelow; binY2 <= lastBinAbove; binY2++) {
+            if (binX2 == binX && binY2 == binY)
+              continue; // skip bin that is currently under consideration
+            
+            // Only take values from (hopefully) valid fits
+            if (h->GetBinContent(binX2, binY2) > 0) {
+              values[binIndex] = h->GetBinContent(binX2, binY2);
+              binIndex++;
+            }
+          }
+        }
+        
+        Double_t temp = getMedianOfNonZeros(values, nNeighbours);
+        if (temp <= 0) {
+          // Only print error message, if there is at least one positive value
+          if (binIndex > 0)
+            printf("Error: Could not eliminate values <= 0 for bin at (%f, %f)!\n",
+                  h->GetXaxis()->GetBinCenter(binX), h->GetYaxis()->GetBinCenter(binY));
+          temp = -1;
+        }
+        
+        printf("Eliminated non-positive value at bin (%f, %f): %f -> %f!\n",
+               h->GetXaxis()->GetBinCenter(binX), h->GetYaxis()->GetBinCenter(binY),
+               h->GetBinContent(binX, binY), temp);
+        h->SetBinContent(binX, binY, temp);
+        h->SetBinError(binX, binY, 1000);
+      }
+    }
+  }
+  
+  delete values;
+}
+
+
+//__________________________________________________________________________________________
+void eliminateOutliers(TH2D* h, Double_t threshold)
+{
+  const Int_t nNeighbours = 8;
+  Double_t* values;
+  values = new Double_t[nNeighbours];
+
+  // Search for outliers (most likely bad fits). Then take all surrounding points in +-1 rows and columns
+  // and take their median (only those bins without (known) fit errors, i.e. bin content > 0).
+  // If the current bin content deviates by more than "threshold" from the median, take the median as the new bin content
+  // of the considered bin.
+  
+  for (Int_t binY = 1; binY <= h->GetYaxis()->GetNbins(); binY++) {
+    Int_t firstBinBelow = TMath::Max(1, binY - 1);
+    Int_t lastBinAbove = TMath::Min(h->GetYaxis()->GetNbins(), binY + 1);
+  
+    for (Int_t binX = 1; binX <= h->GetXaxis()->GetNbins(); binX++) {
+      Int_t firstBinLeft = TMath::Max(1, binX - 1);
+      Int_t lastBinRight = TMath::Min(h->GetXaxis()->GetNbins(), binX + 1);
+    
+      Double_t binContent = h->GetBinContent(binX, binY);
+    
+    
+      
+      // Reset values
+      Int_t binIndex = 0;
+      for (Int_t i = 0; i < nNeighbours; i++)
+        values[i] = 0;
+      
+      for (Int_t binX2 = firstBinLeft; binX2 <= lastBinRight; binX2++) {
+        for (Int_t binY2 = firstBinBelow; binY2 <= lastBinAbove; binY2++) {
+          if (binX2 == binX && binY2 == binY)
+            continue; // skip bin that is currently under consideration
+          
+          // Only take values from (hopefully) valid fits
+          if (h->GetBinContent(binX2, binY2) > 0) {
+            values[binIndex] = h->GetBinContent(binX2, binY2);
+            binIndex++;
+          }
+        }
+      }
+      
+      Double_t temp = getMedianOfNonZeros(values, nNeighbours);
+      if (temp <= 0) {
+        // Only print error message, if there is at least one positive value
+        if (binIndex > 0)
+          printf("Error: Could not eliminate outlier for bin at (%f, %f)!\n",
+                 h->GetXaxis()->GetBinCenter(binX), h->GetYaxis()->GetBinCenter(binY));
+        temp = -1;
+      }
+      else {
+        if (TMath::Abs(binContent - temp) > threshold) {
+          printf("Eliminated outlier at bin (%f, %f): %f -> %f!\n",
+               h->GetXaxis()->GetBinCenter(binX), h->GetYaxis()->GetBinCenter(binY),
+               binContent, temp);
+          h->SetBinContent(binX, binY, temp);
+          h->SetBinError(binX, binY, 1000);
+        }
+      }
+    }
+  }
+  
+  delete values;
+}
+
+
+//__________________________________________________________________________________________
+void addPointToHyperplane(TH2D* h, TLinearFitter* linExtrapolation, Int_t binX, Int_t binY)
+{
+  if (h->GetBinContent(binX, binY) <= binContentThreshold)
+    return; // Reject bins without content (within some numerical precision) or with strange content
+    
+  Double_t coord[2] = {0, 0};
+  coord[0] = h->GetXaxis()->GetBinCenter(binX);
+  coord[1] = h->GetYaxis()->GetBinCenter(binY);
+  Double_t binError = h->GetBinError(binX, binY);
+  if (binError <= 0) {
+    printf("Warning: Trying to add bin in addPointToHyperplane with error (%e) not set (%f, %f), but bin content %f. Maybe fit problem -> Setting large error\n",
+           binError, coord[0], coord[1], h->GetBinContent(binX, binY));
+    binError = 1000;
+  }
+  linExtrapolation->AddPoint(coord, h->GetBinContent(binX, binY, binError));
+}
+
+//__________________________________________________________________________________________
+TH2D* refineHistoViaLinearExtrapolation(TH2D* h, Double_t refineFactorX, Double_t refineFactorY, Int_t mapType, 
+                                        TFile* fSave, Bool_t sigmaMap = kFALSE, Bool_t skipBinsAtBoundaries = kFALSE)
+{
+  if (!h)
+    return 0x0;
+  
+  if (h->GetEntries() <= 0)
+    return 0x0;
+  
+  Int_t nBinsX = h->GetXaxis()->GetNbins();
+  Int_t nBinsY = h->GetYaxis()->GetNbins();
+
+  Int_t firstYbin = 1;
+  
+  if (skipBinsAtBoundaries) {
+    // Remove the two first and the last bin on y-axis
+    nBinsY -= 3;
+    firstYbin = 3; 
+  }
+  
+  // Extrapolate map to larger 1/dEdx using the last 3 points (taking into account skipBinsAtBoundaries). Then: Refine
+  
+  Int_t nBinsYextrapolated = nBinsY + TMath::Ceil((1. / 20. - h->GetYaxis()->GetBinUpEdge(nBinsY)) / h->GetYaxis()->GetBinWidth(nBinsY));
+  // Make sure that the "old" bins stay the same and only some additional bins are added (i.e. bin width should NOT change)
+  Double_t yUpBoundExtrapolated =  h->GetYaxis()->GetBinUpEdge(nBinsY) +  h->GetYaxis()->GetBinWidth(nBinsY) * (nBinsYextrapolated - nBinsY);
+  
+  TH2D* hExtrapolated = new TH2D(Form("%s_%s_extrapolated", h->GetName(), suffixMapType[mapType].Data()), Form("%s (refined)",h->GetTitle()),
+                                 nBinsX, h->GetXaxis()->GetBinLowEdge(1), h->GetXaxis()->GetBinUpEdge(nBinsX),
+                                 nBinsYextrapolated, h->GetYaxis()->GetBinLowEdge(1), yUpBoundExtrapolated);
+  
+  // Copy the old bins and take into account skipBinsAtBoundaries
+  // Get rid of most non-positive bin contents
+  eliminateNonPositivePointsInHisto(h);
+  
+  for (Int_t binX = 1; binX <= nBinsX; binX++)  {
+    for (Int_t binY = 1; binY <= nBinsY; binY++)    {
+      if (h->GetBinContent(binX, binY) > 0 && h->GetBinError(binX, binY) > 0)   {
+        hExtrapolated->SetBinContent(binX, binY, h->GetBinContent(binX, binY));
+        hExtrapolated->SetBinError(binX, binY, h->GetBinError(binX, binY));
+      }
+    }
+    // Default value for extrapolated points is 1
+    for (Int_t binY = nBinsY + 1; binY <= nBinsYextrapolated; binY++)    {
+      hExtrapolated->SetBinContent(binX, binY, 1); 
+      hExtrapolated->SetBinError(binX, binY, 1000); 
+    }
+  }
+  
+  
+  // Do the extrapolation
+  
+  TLinearFitter* linExtrapolation = new TLinearFitter(2, "hyp2", "");
+  
+  for (Int_t binX = 1; binX <= nBinsX; binX++)  {
+    linExtrapolation->ClearPoints();
+    
+    // Last 3 points for fitting for eta map,
+    // but only last 2 points for sigma map, since the resolution
+    // usually levels off around MIP and 3 points used for fitting
+    // would cause an underestimation of the resolution.
+    
+    for (Int_t binY = (sigmaMap ? (nBinsY - 1) : (nBinsY - 2)); binY <= nBinsY; binY++)   {
+      Double_t centerX = hExtrapolated->GetXaxis()->GetBinCenter(binX);
+      Double_t centerY = hExtrapolated->GetYaxis()->GetBinCenter(binY);
+      
+      Int_t oldBinX = h->GetXaxis()->FindBin(centerX);
+      if (oldBinX < 1)  
+        oldBinX = 1;
+      if (oldBinX > nBinsX)
+        oldBinX = nBinsX;
+      
+      Int_t oldBinY = h->GetYaxis()->FindBin(centerY);
+      if (oldBinY < firstYbin)  
+        oldBinY = firstYbin;
+      if (oldBinY > nBinsY)
+        oldBinY = nBinsY;
+      
+      // Neighbours left column
+        if (oldBinX >= 2) {
+          if (oldBinY >= 2) {
+            addPointToHyperplane(h, linExtrapolation, oldBinX - 1, oldBinY - 1);
+          }
+          
+          addPointToHyperplane(h, linExtrapolation, oldBinX - 1, oldBinY);
+          
+          if (oldBinY < nBinsY) {
+            addPointToHyperplane(h, linExtrapolation, oldBinX - 1, oldBinY + 1);
+          }
+        }
+        
+        // Neighbours (and point itself) same column
+        if (oldBinY >= 2) {
+          addPointToHyperplane(h, linExtrapolation, oldBinX, oldBinY - 1);
+        }
+        
+        addPointToHyperplane(h, linExtrapolation, oldBinX, oldBinY);
+        
+        if (oldBinY < nBinsY) {
+          addPointToHyperplane(h, linExtrapolation, oldBinX, oldBinY + 1);
+        }
+        
+        // Neighbours right column
+        if (oldBinX < nBinsX) {
+          if (oldBinY >= 2) {
+            addPointToHyperplane(h, linExtrapolation, oldBinX + 1, oldBinY - 1);
+          }
+          
+          addPointToHyperplane(h, linExtrapolation, oldBinX + 1, oldBinY);
+          
+          if (oldBinY < nBinsY) {
+            addPointToHyperplane(h, linExtrapolation, oldBinX + 1, oldBinY + 1);
+          }
+        }
+    } 
+    // Fit 2D-hyperplane
+    if (linExtrapolation->GetNpoints() <= 0)
+      continue;
+    
+    if (linExtrapolation->Eval() != 0)// EvalRobust -> Takes much, much, [...], much more time (~hours instead of seconds)
+        continue;
+        
+    // ....extrapolation to the rest
+    for (Int_t binY = nBinsY + 1; binY <= nBinsYextrapolated; binY++)    {
+      Double_t centerX = hExtrapolated->GetXaxis()->GetBinCenter(binX);
+      Double_t centerY = hExtrapolated->GetYaxis()->GetBinCenter(binY);
+      
+      // Fill the bin of the refined histogram with the extrapolated value
+      Double_t extrapolatedValue = linExtrapolation->GetParameter(0) + linExtrapolation->GetParameter(1) * centerX
+      + linExtrapolation->GetParameter(2) * centerY;
+      
+      // Do not allow too small or even negative values
+      extrapolatedValue = TMath::Max(binContentThreshold * 2, extrapolatedValue);
+      hExtrapolated->SetBinContent(binX, binY, extrapolatedValue);
+      hExtrapolated->SetBinError(binX, binY, 50); // High, but constant, value => should almost not influence not-extrapolated data      
+    }
+  }
+  
+  eliminateNonPositivePointsInHisto(hExtrapolated);
+  
+  // Normalise map on demand
+  
+  // Use kNoNormalisation for final QA
+  if (mapType == kSmallEtaNormalisation) {
+    normaliseHisto(hExtrapolated, 0., 0.11, kTypeDeltaPrime);
+  }
+  else if (mapType == kLargeEtaNormalisation) {
+    normaliseHisto(hExtrapolated, 0.81, 0.99, kTypeDeltaPrime);
+  }
+  
+  // Interpolate to finer map
+  Int_t nBinsXrefined = nBinsX * refineFactorX;
+  Int_t nBinsYrefined = nBinsYextrapolated * refineFactorY; 
+  
+  TH2D* hRefined = new TH2D(Form("%s_%s_refined", h->GetName(), suffixMapType[mapType].Data()),  Form("%s (refined)",h->GetTitle()),
+                            nBinsXrefined, hExtrapolated->GetXaxis()->GetBinLowEdge(1), hExtrapolated->GetXaxis()->GetBinUpEdge(nBinsX),
+                            nBinsYrefined, hExtrapolated->GetYaxis()->GetBinLowEdge(firstYbin), 
+                            hExtrapolated->GetYaxis()->GetBinUpEdge(nBinsYextrapolated));
+  
+  for (Int_t binX = 1; binX <= nBinsXrefined; binX++)  {
+    Double_t centerX = hRefined->GetXaxis()->GetBinCenter(binX);
+    
+    for (Int_t binY = 1; binY <= nBinsYrefined; binY++)  {
+
+      hRefined->SetBinContent(binX, binY, 1); // Default value is 1
+      
+      Double_t centerY = hRefined->GetYaxis()->GetBinCenter(binY);
+      
+      /*OLD
+      linExtrapolation->ClearPoints();
+      
+      // For interpolation: Just take the corresponding bin from the old histo.
+      // For extrapolation: take the last available bin from the old histo.
+      // If the boundaries are to be skipped, also skip the corresponding bins
+      Int_t oldBinX = hExtrapolated->GetXaxis()->FindBin(centerX);
+      if (oldBinX < 1)  
+        oldBinX = 1;
+      if (oldBinX > nBinsX)
+        oldBinX = nBinsX;
+      
+      Int_t oldBinY = hExtrapolated->GetYaxis()->FindBin(centerY);
+      if (oldBinY < firstYbin)  
+        oldBinY = firstYbin;
+      if (oldBinY > nBinsYextrapolated)
+        oldBinY = nBinsYextrapolated;
+      
+      // Neighbours left column
+      if (oldBinX >= 2) {
+        if (oldBinY >= 2) {
+          addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX - 1, oldBinY - 1);
+        }
+        
+        addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX - 1, oldBinY);
+        
+        if (oldBinY < nBinsYextrapolated) {
+          addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX - 1, oldBinY + 1);
+        }
+      }
+      
+      // Neighbours (and point itself) same column
+      if (oldBinY >= 2) {
+        addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX, oldBinY - 1);
+      }
+        
+      addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX, oldBinY);
+        
+      if (oldBinY < nBinsYextrapolated) {
+        addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX, oldBinY + 1);
+      }
+      
+      // Neighbours right column
+      if (oldBinX < nBinsX) {
+        if (oldBinY >= 2) {
+          addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX + 1, oldBinY - 1);
+        }
+        
+        addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX + 1, oldBinY);
+        
+        if (oldBinY < nBinsYextrapolated) {
+          addPointToHyperplane(hExtrapolated, linExtrapolation, oldBinX + 1, oldBinY + 1);
+        }
+      }
+      
+      
+      // Fit 2D-hyperplane
+      if (linExtrapolation->GetNpoints() <= 0)
+        continue;
+        
+      if (linExtrapolation->Eval() != 0)// EvalRobust -> Takes much, much, [...], much more time (~hours instead of seconds)
+        continue;
+      
+      // Fill the bin of the refined histogram with the extrapolated value
+      Double_t interpolatedValue = linExtrapolation->GetParameter(0) + linExtrapolation->GetParameter(1) * centerX
+                                 + linExtrapolation->GetParameter(2) * centerY;
+      */
+      Double_t interpolatedValue = hExtrapolated->Interpolate(centerX, centerY);
+      hRefined->SetBinContent(binX, binY, interpolatedValue);      
+    }
+  } 
+  
+  
+  // Problem: Interpolation does not work before/beyond center of first/last bin (as the name suggests).
+  // Therefore, for each row in dEdx: Take last bin from old map and interpolate values from center and edge.
+  // Assume line through these points and extropolate to last bin of refined map
+  const Double_t firstOldXbinUpEdge = hExtrapolated->GetXaxis()->GetBinUpEdge(1);
+  const Double_t firstOldXbinCenter = hExtrapolated->GetXaxis()->GetBinCenter(1);
+  
+  const Double_t oldXbinHalfWidth = firstOldXbinUpEdge - firstOldXbinCenter;
+  
+  const Double_t lastOldXbinLowEdge = hExtrapolated->GetXaxis()->GetBinLowEdge(hExtrapolated->GetNbinsX());
+  const Double_t lastOldXbinCenter = hExtrapolated->GetXaxis()->GetBinCenter(hExtrapolated->GetNbinsX());
+  
+  for (Int_t binY = 1; binY <= nBinsYrefined; binY++)  {
+    Double_t centerY = hRefined->GetYaxis()->GetBinCenter(binY);
+    
+    const Double_t interpolatedCenterFirstXbin = hExtrapolated->Interpolate(firstOldXbinCenter, centerY);
+    const Double_t interpolatedUpEdgeFirstXbin = hExtrapolated->Interpolate(firstOldXbinUpEdge, centerY);
+    
+    const Double_t extrapolationSlopeFirstXbin = (interpolatedUpEdgeFirstXbin - interpolatedCenterFirstXbin) / oldXbinHalfWidth;
+    const Double_t extrapolationOffsetFirstXbin = interpolatedCenterFirstXbin;
+    
+    
+    const Double_t interpolatedCenterLastXbin = hExtrapolated->Interpolate(lastOldXbinCenter, centerY);
+    const Double_t interpolatedLowEdgeLastXbin = hExtrapolated->Interpolate(lastOldXbinLowEdge, centerY);
+    
+    const Double_t extrapolationSlopeLastXbin = (interpolatedCenterLastXbin - interpolatedLowEdgeLastXbin) / oldXbinHalfWidth;
+    const Double_t extrapolationOffsetLastXbin = interpolatedCenterLastXbin;
+
+    for (Int_t binX = 1; binX <= nBinsXrefined; binX++)  {
+      Double_t centerX = hRefined->GetXaxis()->GetBinCenter(binX);
+     
+      if (centerX < firstOldXbinCenter) {
+        Double_t extrapolatedValue = extrapolationOffsetFirstXbin + (centerX - firstOldXbinCenter) * extrapolationSlopeFirstXbin;
+        hRefined->SetBinContent(binX, binY, extrapolatedValue);      
+      }
+      else if (centerX <= lastOldXbinCenter) {
+        continue;
+      }
+      else {
+        Double_t extrapolatedValue = extrapolationOffsetLastXbin + (centerX - lastOldXbinCenter) * extrapolationSlopeLastXbin;
+        hRefined->SetBinContent(binX, binY, extrapolatedValue);     
+      }
+    }
+  } 
+  
+  delete linExtrapolation;
+  
+  
+  if (fSave)    {
+    fSave->cd();
+    hExtrapolated->Write();
+  }
+  
+  delete hExtrapolated;
+  
+  return hRefined;
+}
+
+
+//__________________________________________________________________________________________
+TFitResult*  doubleGaussFit(TH1D* h, Double_t currentMeanMomentum, TSpline3* splPr, TSpline3* splPi, TString fitOption = "QNS") 
+{
+  Double_t lowThreshold = 0.6;
+  Double_t highThreshold = 1.6;
+  FindFitRange(h, lowThreshold, highThreshold);
+  TFitResultPtr result = h->Fit("gaus", "QNS", "", lowThreshold, highThreshold);
+  
+  Double_t contaminationPeakMean = splPi->Eval(currentMeanMomentum / massPion) / splPr->Eval(currentMeanMomentum / massProton);
+
+  if (contaminationPeakMean < h->GetXaxis()->GetBinLowEdge(1) ||
+      contaminationPeakMean > h->GetXaxis()->GetBinUpEdge(h->GetNbinsX())) {
+    return ((Int_t)result == 0) ? (TFitResult*)result.Get()->Clone() : 0x0;
+  }
+  // Estimate parameters for doubleGauss fit
+  Double_t estimatedMean = 0;
+  Double_t estimatedSigma = 0;
+  Double_t estimatedYield = 0;
+  
+  Double_t chi2oneGauss = 0;
+  Int_t NDFoneGauss = 0;
+  Double_t reducedChi2oneGauss = 0;
+  
+  if ((Int_t) result == 0) {
+    estimatedMean = result->GetParams()[1];
+    estimatedSigma = result->GetParams()[2];
+    estimatedYield = result->GetParams()[0];
+    
+    chi2oneGauss = result->Chi2();
+    NDFoneGauss = result->Ndf();
+    reducedChi2oneGauss = (NDFoneGauss > 0) ? chi2oneGauss / NDFoneGauss : -1;
+  }
+  else {
+    estimatedMean = h->GetMean();
+    estimatedSigma = h->GetRMS();
+    estimatedYield = (estimatedSigma > 0) ? (h->Integral("width") / estimatedSigma) : h->GetEntries();
+  }
+  
+  TF1* doubleGaus = new TF1("doubleGaus", "[0]*TMath::Gaus(x,[1],[2],0)+[3]*TMath::Gaus(x,[4],[2],0)", 0.6, 1.6);
+
+  estimatedMean = TMath::Max(0.6, estimatedMean);
+  estimatedYield = TMath::Max(1., estimatedYield);
+  
+  Double_t newPars[5] = { estimatedYield, estimatedMean, estimatedSigma, estimatedYield / 10., contaminationPeakMean };
+  doubleGaus->SetParameters(newPars);
+  doubleGaus->SetParLimits(0, 0.01 * estimatedYield, 100. * estimatedYield);//TODO 0., 100. * estimatedYield);
+  doubleGaus->SetParLimits(1, estimatedMean - 0.05, estimatedMean + 0.05);//TODO 0.6, 1.6);
+  doubleGaus->SetParLimits(2, 0, 100. * estimatedSigma);
+  doubleGaus->SetParLimits(3, 0, 0.8 * estimatedYield);
+  doubleGaus->SetParLimits(4, contaminationPeakMean - 0.15, contaminationPeakMean + 0.15);//TODO doubleGaus->SetParLimits(4, 0.6, 1.6);
+  TFitResultPtr result2 = h->Fit(doubleGaus, fitOption.Data());
+
+  printf("\n%f -> %f\n", currentMeanMomentum, contaminationPeakMean);//TODO NOW NOW NOW
+  printf("%f, %f, %f, %f, %f\n%f, %f, %f\n", result2->GetParams()[0], result2->GetParams()[1], result2->GetParams()[2], result2->GetParams()[3], result2->GetParams()[4], result->GetParams()[0], result->GetParams()[1], result->GetParams()[2]); printedSomething = kTRUE;//TODO NOW NOW NOW
+  if ((Int_t)result2 == 0) {
+    Double_t chi2doubleGauss = 0;
+    Int_t NDFdoubleGauss = 0;
+    Double_t reducedChi2doubleGauss = 0;
+    
+    chi2doubleGauss = result2->Chi2();
+    NDFdoubleGauss = result2->Ndf();
+    reducedChi2doubleGauss = (NDFdoubleGauss > 0) ? chi2doubleGauss / NDFdoubleGauss : -1;
+    
+    printf("%f / %d = %f\n %f / %d = %f\n\n", chi2oneGauss, NDFoneGauss, reducedChi2oneGauss, chi2doubleGauss, NDFdoubleGauss, reducedChi2doubleGauss); printedSomething = kTRUE;//TODO NOW NOW NOW
+    // Only take double gauss result, if (valid) reduced chi2 is better than that of the one gauss fit
+    if (reducedChi2oneGauss < 0 || (reducedChi2doubleGauss > 0 && reducedChi2doubleGauss <= reducedChi2oneGauss))
+      return (TFitResult*)result2.Get()->Clone();
+  }
+  
+  // If fit failed, return results of standard fit instead
+  return ((Int_t)result == 0) ? (TFitResult*)result.Get()->Clone() : 0x0;
+}
+
+
+//__________________________________________________________________________________________
+TFitResult* extractClusterDependence(TH3D** hPreMapClusterResolved, Int_t numClusterBins, Int_t clusterLowBound, Int_t clusterUpBound,
+                                     Int_t binX, Int_t binY, Int_t binYhigh, Double_t c0, TFile* fSave, TSpline3* splPr, TSpline3* splPi)
+{
+  TH1D* hClusMean = new TH1D(Form("hClusMean_tanTheta_%f_pTPC_%f", hPreMapClusterResolved[0]->GetXaxis()->GetBinCenter(binX),
+                                  hPreMapClusterResolved[0]->GetYaxis()->GetBinLowEdge(binY)),
+                             Form("#Delta' vs. ncl (tan(#Theta) %f, p_{TPC} %f);ncl;#Delta'", hPreMapClusterResolved[0]->GetXaxis()->GetBinCenter(binX),
+                                  hPreMapClusterResolved[0]->GetYaxis()->GetBinLowEdge(binY)),
+                             numClusterBins, clusterLowBound, clusterUpBound);
+  
+  TH1D* hClusSigmaRelGauss = new TH1D(Form("hClusSigmaRelGauss_tanTheta_%f_pTPC_%f", hPreMapClusterResolved[0]->GetXaxis()->GetBinCenter(binX),
+                                           hPreMapClusterResolved[0]->GetYaxis()->GetBinLowEdge(binY)),
+                                      Form("#sigma_{rel, Gauss} vs. ncl (tan(#Theta) %f, p_{TPC} %f);ncl;#sigma_{rel, Gauss}",
+                                           hPreMapClusterResolved[0]->GetXaxis()->GetBinCenter(binX),
+                                           hPreMapClusterResolved[0]->GetYaxis()->GetBinLowEdge(binY)),
+                                      numClusterBins, clusterLowBound, clusterUpBound);
+
+  TH1D* hClusSigmaRel = new TH1D(Form("hClusSigmaRel_tanTheta_%f_pTPC_%f", hPreMapClusterResolved[0]->GetXaxis()->GetBinCenter(binX),
+