From f2ce36363f7d5fa19baf7c639b04430ef1164f10 Mon Sep 17 00:00:00 2001
From: Alexander Zinchenko <Alexander.Zinchenko@jinr.ru>
Date: Wed, 2 Nov 2016 20:33:10 +0400
Subject: [PATCH] MLEM (Bayesian unfolding) cluster finder

---
 tpc/CMakeLists.txt              |    1 +
 tpc/MpdTpcClusterFinderMlem.cxx | 1255 +++++++++++++++++++++++++++++++
 tpc/MpdTpcClusterFinderMlem.h   |  114 +++
 tpc/tpcLinkDef.h                |    1 +
 4 files changed, 1371 insertions(+)
 create mode 100644 tpc/MpdTpcClusterFinderMlem.cxx
 create mode 100644 tpc/MpdTpcClusterFinderMlem.h

diff --git a/tpc/CMakeLists.txt b/tpc/CMakeLists.txt
index 63dbdc2..d73f575 100644
--- a/tpc/CMakeLists.txt
+++ b/tpc/CMakeLists.txt
@@ -51,6 +51,7 @@ MpdTpcSectorGeo.cxx
 MpdTpcDigitizerAZ.cxx
 MpdTpcClusterFinderAZ.cxx
 MpdTPCpid.cxx
+MpdTpcClusterFinderMlem.cxx
 )
 
 # fill list of header files from list of source files
diff --git a/tpc/MpdTpcClusterFinderMlem.cxx b/tpc/MpdTpcClusterFinderMlem.cxx
new file mode 100644
index 0000000..e8b8bf7
--- /dev/null
+++ b/tpc/MpdTpcClusterFinderMlem.cxx
@@ -0,0 +1,1255 @@
+//-----------------------------------------------------------
+// File and Version Information:
+// $Id$
+//
+// Description:
+//      Implementation of class MpdTpcClusterFinderMlem
+//      see MpdTpcClusterFinderMlem.h for details
+//
+// Environment:
+//      Software developed for the MPD Detector at NICA.
+//
+// Author List:
+//      Alexandr Zinchenko LHEP, JINR, Dubna - 11-January-2016
+//
+//-----------------------------------------------------------
+
+// This Class' Header ------------------
+#include "MpdTpcClusterFinderMlem.h"
+
+// Collaborating Class Headers --------
+#include "MpdTpc2dCluster.h"
+#include "MpdTpcDigit.h"
+#include "MpdTpcHit.h"
+#include "MpdTpcSectorGeo.h"
+#include "TpcPoint.h"
+
+#include "FairRootManager.h"
+
+#include <TClonesArray.h>
+#include <TFile.h>
+#include <TH2.h>
+#include <TMath.h>
+#include <TMatrixD.h>
+#include <TROOT.h>
+#include <TSystem.h>
+
+// C/C++ Headers ----------------------
+#include <iostream>
+#include <math.h>
+#include <set>
+#include <vector>
+
+using namespace std;
+
+static Bool_t SortPix(const MpdTpcClusterFinderMlem::pixel i, 
+		      const MpdTpcClusterFinderMlem::pixel j); // sorting function
+
+//__________________________________________________________________________
+
+MpdTpcClusterFinderMlem::MpdTpcClusterFinderMlem()
+  : FairTask("TPC Cluster finder Mlem"), fPersistence(kFALSE)
+{
+  /*
+  std::string tpcGasFile = gSystem->Getenv("VMCWORKDIR");
+  tpcGasFile += "/geometry/Ar-90_CH4-10.asc";
+  fGas= new TpcGas(tpcGasFile, 130);
+  std::cout<<*fGas<<std::endl;
+  */
+}
+
+//__________________________________________________________________________
+
+MpdTpcClusterFinderMlem::~MpdTpcClusterFinderMlem()
+{
+  //delete fGas;
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::FinishTask()
+{
+  for (Int_t i = 0; i < fgkNsec2; ++i) {
+    //fPrimArray[i]->Delete();
+    delete [] fDigiSet[i];
+  }
+}
+
+//__________________________________________________________________________
+
+InitStatus MpdTpcClusterFinderMlem::Init()
+{
+
+  // Create containers for digits
+  fSecGeo = MpdTpcSectorGeo::Instance();
+  //Int_t nRows = MpdTpcSectorGeo::Instance()->NofRows();
+  Int_t nRows = fSecGeo->NofRows();
+  for (Int_t i = 0; i < fgkNsec2; ++i) fDigiSet[i] = new set<Int_t> [nRows];
+
+  //Get ROOT Manager
+  FairRootManager* ioman = FairRootManager::Instance();
+
+  if (ioman == 0) {
+    Error("MpdTpcClusterFinderMlem::Init","RootManager not instantiated!");
+    return kERROR;
+  }
+  
+  // Get input collection
+  fDigiArray = (TClonesArray*) ioman->GetObject("MpdTpcDigit");
+  
+  if (fDigiArray == 0) {
+    Error("TpcClusterFinderMlem::Init","Array of digits not found!");
+    return kERROR;
+  }
+  
+  // Create and register output array
+  //SetPersistence();
+  fClusArray = new TClonesArray("MpdTpc2dCluster"); 
+  ioman->Register("TpcCluster", "Tpc", fClusArray, fPersistence);
+  fHitArray = new TClonesArray("MpdTpcHit"); 
+  ioman->Register("TpcRecPoint", "Tpc", fHitArray, fPersistence);
+
+  return kSUCCESS;
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::Exec(Option_t* opt)
+{
+
+  fClusArray->Delete();
+  fHitArray->Delete();
+  const Int_t nSec = fgkNsec2 / 2; // number of TPC readout sectors
+  // Clear digi containers
+  Int_t nRows = fSecGeo->NofRows();
+  for (Int_t i = 0; i < fgkNsec2; ++i) {
+    for (Int_t j = 0; j < nRows; ++j) fDigiSet[i][j].clear();
+  }
+  
+  // Fill digi containers
+  Int_t nDigis = fDigiArray->GetEntriesFast();
+  cout << " Total number of digits: " << nDigis << endl;
+  for (Int_t i = 0; i < nDigis; ++i) {
+    MpdTpcDigit *digi = (MpdTpcDigit*) fDigiArray->UncheckedAt(i);
+    Int_t isec = digi->GetSector();
+    Int_t irow = digi->GetRow();
+    fDigiSet[isec][irow].insert(i);
+  }
+  
+  Int_t nSum = 0;
+  // Loop over sectors
+  for (Int_t isec = 0; isec < fgkNsec2; ++isec) {
+
+    // Loop over padrows
+    for (Int_t irow = 0; irow < nRows; ++irow) {
+      if (fDigiSet[isec][irow].size() == 0) continue;
+      //cout << " Sector, row, digits: " << isec << " " << irow << " " << fDigiSet[isec][irow].size() << endl; 
+      ProcessPadrow(isec, irow);
+      nSum += fDigiSet[isec][irow].size();
+    }
+  }
+  cout << " Control sum: " << nSum << endl;
+
+  // Find hits
+  FindHits();
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::ProcessPadrow(Int_t isec, Int_t irow)
+{
+  // Process one padrow of a sector
+
+  memset(fFlags, 0, sizeof(fFlags[0][0]) * fgkNpads * fgkNtimes);
+
+  set<Int_t>::iterator it = fDigiSet[isec][irow].begin();
+
+  for ( ; it != fDigiSet[isec][irow].end(); ++it) {
+    MpdTpcDigit *digi = (MpdTpcDigit*) fDigiArray->UncheckedAt(*it);
+    Int_t ipad = digi->GetPad(), itime = digi->GetTimeBin();
+    if (ipad >= fgkNpads) Fatal("ProcessPadrow", "Too few pads!!! %i %i", ipad, fgkNpads);
+    if (itime >= fgkNtimes) Fatal("ProcessPadrow", "Too few time bins!!! %i %i", itime, fgkNtimes);
+    fCharges[ipad][itime] = digi->GetAdc();
+    //fDigis[ipad][itime] = *it;
+    fDigis[ipad][itime] = digi->GetOrigin(); // store trackID with max charge contribution
+    fFlags[ipad][itime] = 1;
+  }
+  
+  // Find (pre)clusters
+  Int_t nclus0 = fClusArray->GetEntriesFast(), nclus = nclus0;
+  for (Int_t ipad = 0; ipad < fgkNpads; ++ipad) {
+    for (Int_t itime = 0; itime < fgkNtimes; ++itime) {
+      if (fFlags[ipad][itime] <= 0) continue;
+      // New cluster
+      MpdTpc2dCluster* clus = new ((*fClusArray)[nclus++]) MpdTpc2dCluster(irow, isec); 
+      //clus->Insert(fDigis[ipad][itime], irow, ipad, itime, fCharges[ipad][itime]);
+      clus->Insert(fDigis[ipad][itime], irow, ipad+2, itime, fCharges[ipad][itime]); // extra shift
+      clus->SetID(fDigis[ipad][itime]); // trackID
+      clus->SetErrY(fCharges[ipad][itime]); // ADC counts
+      fFlags[ipad][itime] = -1;
+      for (Int_t ip = 0; ip < 2; ++ip) {
+	for (Int_t it = 0; it < 2; ++it) {
+	  if (it == ip) continue;
+	  Int_t ip1 = ipad + ip, it1 = itime + it;
+	  if (ip1 >= fgkNpads) continue;
+	  if (it1 >= fgkNtimes) continue;
+	  if (fFlags[ip1][it1] <= 0) continue;
+	  NextPixel(clus, ip1, it1);
+	}
+      }
+    }
+  }
+  //cout << " Found preclusters: " << nclus - nclus0 << endl;
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::NextPixel(MpdTpc2dCluster* clus, Int_t ipad, Int_t itime)
+{
+  // Add next pixel to the cluster
+
+  //clus->Insert(fDigis[ipad][itime], clus->Row(), ipad, itime, fCharges[ipad][itime]);
+  clus->Insert(fDigis[ipad][itime], clus->Row(), ipad+2, itime, fCharges[ipad][itime]); // extra shift
+  clus->SetID (TMath::Min(clus->ID(),fDigis[ipad][itime])); // min trackID
+  clus->SetErrY (TMath::Max(Double_t(clus->GetErrY()),fCharges[ipad][itime])); // max ADC counts
+  fFlags[ipad][itime] = -1;
+  for (Int_t ip = -1; ip < 2; ++ip) {
+    for (Int_t it = -1; it < 2; ++it) {
+      if (TMath::Abs(it) == TMath::Abs(ip)) continue;
+      Int_t ip1 = ipad + ip, it1 = itime + it;
+      if (ip1 < 0 || ip1 >= fgkNpads) continue;
+      if (it1 < 0 || it1 >= fgkNtimes) continue;
+      if (fFlags[ip1][it1] <= 0) continue;
+      NextPixel(clus, ip1, it1);
+    }
+  }
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::FindHits()
+{
+  // Reconstruct hits (find local maxima)
+
+  /*
+    fCharges[ipad][itime] = digi->GetAdc();
+    //fDigis[ipad][itime] = *it;
+    fDigis[ipad][itime] = digi->GetOrigin(); // store trackID with max charge contribution
+    fFlags[ipad][itime] = 1;
+    */
+  TVector3 p3loc, p3glob, p3err(0.05,0.0,0.1);
+  Int_t nclus = fClusArray->GetEntriesFast(), ihit = 0;
+
+  for (Int_t iclus = 0; iclus < nclus; ++iclus) {
+    MpdTpc2dCluster* clus = (MpdTpc2dCluster*) fClusArray->UncheckedAt(iclus);
+    Int_t nDigis = clus->NDigits();
+    //if (nDigis == 1) continue; // skip too small clusters
+
+    memset(fFlags, 0, sizeof(fFlags[0][0]) * fgkNpads * fgkNtimes);
+    for (Int_t idig = 0; idig < nDigis; ++idig) {
+      Int_t ipad = clus->Col(idig);
+      Int_t itime = clus->Bkt(idig);
+      fCharges[ipad][itime] = clus->Adc(idig);
+      fFlags[ipad][itime] = 1;
+      fDigis[ipad][itime] = idig;
+    }
+
+    // Exclude pads which are not local maxima
+    Int_t novfw = 0;
+    for (Int_t idig = 0; idig < nDigis; ++idig) {
+      Int_t ipad = clus->Col(idig);
+      Int_t itime = clus->Bkt(idig);
+      if (clus->Adc(idig) > fgkOvfw - 1) ++novfw;
+      for (Int_t ip = -1; ip < 2; ++ip) {
+	for (Int_t it = -1; it < 2; ++it) {
+	  if (TMath::Abs(it) == TMath::Abs(ip)) continue; // exclude diagonals
+	  if (it == 0 && ip == 0) continue;
+	  Int_t ip1 = ipad + ip, it1 = itime + it;
+	  if (ip1 < 0 || ip1 >= fgkNpads) continue;
+	  if (it1 < 0 || it1 >= fgkNtimes) continue;
+	  if (fFlags[ip1][it1] == 0) continue;
+	  if (clus->Adc(idig) < fCharges[ip1][it1]) { fFlags[ipad][itime] = -1; break; }
+	  else if (clus->Adc(idig) == fCharges[ip1][it1] && fFlags[ip1][it1] > 0) { fFlags[ipad][itime] = -1; break; }
+	}
+      }
+    }
+    if (novfw) clus->SetOverflows(novfw);
+
+    multimap<Double_t,Int_t> localMax;
+    for (Int_t idig = 0; idig < nDigis; ++idig) {
+      Int_t ipad = clus->Col(idig);
+      Int_t itime = clus->Bkt(idig);
+      if (fFlags[ipad][itime] <= 0) continue;
+      localMax.insert(pair<Double_t,Int_t>(clus->Adc(idig),idig));
+      //cout << clus->Col(idig) << " " << clus->Bkt(idig) << " " << clus->Adc(idig) << endl;
+    }
+    //cout << " Local max: " << clus->GetSect() << " " << clus->Row() << " " << localMax.size() << endl;
+
+    Int_t nLocMax0 = localMax.size();
+    clus->SetUniqueID(nLocMax0); // for testing
+
+    // Check for edge effect - maximum digit at the sector edge
+    Int_t edge = 0;
+    Int_t idig = localMax.rbegin()->second;
+    Int_t ipad = clus->Col(idig);
+    if (ipad-2 == 0 || ipad-2 == 2*fSecGeo->NPadsInRows()[clus->Row()] - 1) edge = 1; // remember extra shift !!!
+    //if (clus->GetNumPads() > 1) edge = -edge;
+    if (edge > 0) {
+      // Add "virtual" digit
+      Int_t itime = clus->Bkt(idig), ipadv = 0, ipadn = -9;
+      if (clus->GetNumPads() == 1) ipadv = (ipad-2 == 0) ? ipad + 1 : ipad - 1;
+      else ipadv = (ipad-2 == 0) ? ipad - 1 : ipad + 1;
+      fFlags[ipadv][itime] = -1;
+      //if (clus->GetNumPads() == 1) fCharges[ipadv][itime] = 20; // below threshold
+      if (clus->GetNumPads() == 1) fCharges[ipadv][itime] = TMath::Min(29.0,fCharges[ipad][itime]*0.1); // below threshold
+      //else fCharges[ipadv][itime] = fCharges[ipad][itime] * 0.9;
+      else {
+	ipadn = (ipad-2 == 0) ? ipad + 1 : ipad - 1;
+	fCharges[ipadv][itime] = fCharges[ipadn][itime];
+	//fCharges[ipadv][itime] = fCharges[ipad][itime] * 1.5;
+	//fCharges[ipadv][itime] = TMath::Min (fCharges[ipadv][itime], 1.0*fgkOvfw);
+      }
+      fDigis[ipadv][itime] = fDigis[ipad][itime];
+      clus->Insert(fDigis[ipadv][itime], clus->Row(), ipadv, itime, fCharges[ipadv][itime]);
+      //cout << " Edge: " << clus->Row() << " " << clus->GetId() << " " << ipad << endl; 
+      clus->SetVirtual(); // flag "virtual" pad addition
+    }
+    if (edge) clus->SetEdge(); // flag edge effect
+
+    if (edge || localMax.size() > 1) {
+      // Run MLEM procedure
+      //PeakAndValley(clus, localMax);
+      Mlem(iclus, localMax);
+      clus->SetFlag(clus->Flag()|1); // flag MLEM procedure usage
+      continue;
+    }
+
+    map<Int_t,Double_t> mapIdQ;
+    Double_t padMean = 0, timeMean = 0, adcTot = 0, sum2t = 0, sum2p = 0;
+
+    // Process simple cluster (only 1 local max)
+    for (Int_t idig = 0; idig < nDigis; ++idig) {
+      Int_t ip = clus->Col(idig);
+      Int_t it = clus->Bkt(idig);
+      padMean += ip * fCharges[ip][it];
+      timeMean += it * fCharges[ip][it];
+      adcTot += fCharges[ip][it];
+      Int_t id = clus->Sec(fDigis[ip][it]);
+      //if (mapIdQ.find(id) == mapIdQ.end()) mapIdQ[id] = fCharges[ip][it];
+      //else mapIdQ[id] = mapIdQ[id] + fCharges[ip][it];
+      //else mapIdQ[id] = TMath::Max (mapIdQ[id], fCharges[ip][it]);
+      if (mapIdQ.find(id) == mapIdQ.end()) mapIdQ[id] = 1;
+      else mapIdQ[id] = mapIdQ[id] + 1;
+      
+      sum2t += it * it * fCharges[ip][it];
+      sum2p += ip * ip * fCharges[ip][it];
+    }
+
+    //padMean /= adcTot;
+    padMean = padMean / adcTot - 2; // correct for shift
+    timeMean /= adcTot;
+
+    Double_t xloc = fSecGeo->Pad2Xloc(padMean,clus->Row());
+    Int_t padID = fSecGeo->PadID(clus->GetSect() % fSecGeo->NofSectors(), clus->Row());
+    Double_t yloc = fSecGeo->LocalPadPosition(padID).Y();
+    Double_t zloc = fSecGeo->TimeBin2Z(timeMean);
+    p3loc.SetXYZ(xloc, yloc, zloc);
+    Double_t rmsZ = TMath::Sqrt (sum2t/adcTot - timeMean*timeMean);
+    Double_t rmsX = TMath::Sqrt (sum2p/adcTot - padMean*padMean);
+    //p3err[1] = fSecGeo->TimeBin2Z(rms); // to pass the value
+    //p3err[1] = rms;
+    //cout << " Result: " << nLocMax0 << " " << pixels.size() << " " << xloc << " " << zloc << endl;
+
+    // Apply corrections
+    TVector3 p3errCor(p3err);
+    CorrectReco(p3loc, p3errCor, clus->GetNumPads(), adcTot);
+    
+    if (clus->GetSect() >= fSecGeo->NofSectors()) p3loc[2] = -p3loc[2];
+    fSecGeo->Local2Global(clus->GetSect(), p3loc, p3glob);
+    
+    // Dip angle correction (for interaction point with Z = 0)
+    Double_t dip = TMath::ATan2 (TMath::Abs(p3glob[2]),p3glob.Pt()) * TMath::RadToDeg();
+    p3errCor[2] = TMath::Max (p3errCor[2], 0.116 - 0.0046 * dip + 0.00015 * dip * dip);
+    p3errCor[2] = TMath::Min (p3errCor[2], 0.5);
+    // Correct Z-coordinate for rows = 0 and 27
+    //if ((clus->Row() == 0 || clus->Row() == 27) && rmsZ > 1.0) {
+    if (clus->Row() == 0 && rmsZ > 1.0) {
+      Double_t zcor = 0;
+      if (clus->Row() == 0) zcor = -0.011 + 0.002 * dip;
+      else zcor = -0.029 + 0.005 * dip + 3.886e-5 * dip * dip;
+      zcor = TMath::Max (zcor, 0.);
+      zcor = TMath::Min (zcor, 0.6);
+      p3loc[2] -= TMath::Sign (zcor, p3loc[2]);
+      p3glob[2] -= TMath::Sign (zcor, p3glob[2]);
+    }
+    
+    ihit = fHitArray->GetEntriesFast();
+    MpdTpcHit* hit = new ((*fHitArray)[ihit]) MpdTpcHit(padID, p3glob, p3errCor, iclus); 
+    hit->SetLayer(clus->Row());
+    hit->SetLocalPosition(p3loc); // point position
+    hit->SetEnergyLoss(adcTot);
+    Int_t ireg = (clus->Row() < fSecGeo->NofRowsReg(0)) ? 0 : 1;
+    Double_t step = fSecGeo->PadHeight(ireg);
+    hit->SetStep(step);
+    hit->SetModular(1); // modular geometry flag
+    hit->SetPad(Int_t(padMean));
+    hit->SetBin(Int_t(timeMean));
+    hit->SetRMS(rmsX, 0);
+    hit->SetRMS(rmsZ, 1);
+    hit->SetNdigits(nDigis);
+    
+    // !!! Warning: FairLinks are not persistent !!! 
+    //hit->AddLink(FairLink(MpdTpcHit::PointIndex, pointIndx));
+    vector<Int_t> vecDig;
+    // Store maximum 3 track IDs with the highest charge contribution
+    multimap<Double_t,Int_t> mapDig;
+    for (map<Int_t,Double_t>::iterator mit = mapIdQ.begin(); mit != mapIdQ.end(); ++mit) 
+      mapDig.insert(pair<Double_t,Int_t>(-mit->second,mit->first));
+    multimap<Double_t,Int_t>::iterator mit = mapDig.begin();
+    while (mit != mapDig.end() && vecDig.size() < 9) {
+      //while (mit != mapDig.end() && vecDig.size() < 1) {
+      vecDig.push_back(mit->second);
+      ++mit;
+    }
+    Int_t ndig = vecDig.size();
+    /*
+      for (Int_t idig = 0; idig < ndig; ++idig)
+      hit->AddLink(FairLink(MpdTpcHit::MCTrackIndex, clus->Sec(vecDig[idig]))); // trackID stored in Sec
+    */
+    for (Int_t idig = 0; idig < ndig; ++idig) {
+      hit->AddLink(FairLink(MpdTpcHit::MCTrackIndex, vecDig[idig], idig)); // weight = idig
+      hit->AddID(vecDig[idig]);
+    }
+    //cout << hit->GetNLinks() << " " << *(vecDig.begin()) << " " << hit->GetLinksWithType(MpdTpcHit::MCTrackIndex).GetLink(0).GetIndex() << " " << hit->GetLinksWithType(MpdTpcHit::MCTrackIndex).GetLink(hit->GetNLinks()-1).GetIndex() << endl;
+
+  } // for (Int_t iclus = 0; iclus < nclus;
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::PeakAndValley(const MpdTpc2dCluster* clus, multimap<Double_t,Int_t> &localMax)
+{
+  // Apply peak-and-valley cuts to remove some local maxima
+
+  const Double_t ratio = 1.3;
+  multimap<Double_t,Int_t>::reverse_iterator rit = localMax.rbegin(), rit1;
+  ++rit;
+
+  for ( ; rit != localMax.rend(); ++rit) {
+    Int_t idig = rit->second;
+    Int_t ipad = clus->Col(idig);
+    Int_t itime = clus->Bkt(idig);
+    if (fFlags[ipad][itime] <= 0) continue; // merged local max
+
+    // Loop over higher peaks
+    rit1 = localMax.rbegin();
+    for ( ; rit1 != rit; ++rit1) {
+      Int_t idig0 = rit1->second;
+      Int_t ipad0 = clus->Col(idig0);
+      Int_t itime0 = clus->Bkt(idig0);
+      if (fFlags[ipad0][itime0] <= 0) continue; // merged local max
+
+      Int_t dpad = ipad - ipad0, dt = itime - itime0;
+      Int_t i0 = itime0, i1 = itime, j0 = ipad0, j1 = ipad, intime = 1;
+      if (TMath::Abs(dpad) > TMath::Abs(dt)) i0 = ipad0, i1 = ipad, j0 = itime0, j1 = itime, intime = 0;
+      Int_t stepi = TMath::Sign(1,i1-i0), stepj = TMath::Sign(1,j1-j0);
+      
+      //Int_t valOk = 0;
+      Int_t merge = 0;
+      //if (TMath::Abs(dpad) <= 1 && TMath::Abs(dt) <= 1) merge = 1;
+      //else {
+      if (TMath::Abs(dpad) <= 1 && TMath::Abs(dt) <= 1) {
+	if (fCharges[ipad-dpad][itime] > fCharges[ipad][itime] / ratio ||
+	    fCharges[ipad][itime-dt] > fCharges[ipad][itime] / ratio) merge = 1;
+      } else {
+	for (Int_t ii = i0 + stepi; ii != i1; ii += stepi) {
+	  merge = 0;
+	  for (Int_t jj = j0; jj != j1 + stepj; jj += stepj) {
+	    if (TMath::Abs(jj-j0) > TMath::Abs(ii-i0)) continue;
+	    if (TMath::Abs(jj-j1) > TMath::Abs(ii-i1)) continue;
+	    Int_t ip = ii, it = jj;
+	    if (intime) ip = jj, it = ii;
+	    //if (fCharges[ip][it] < fCharges[ipad][itime] / ratio) { valOk = 1; break; }
+	    if (fCharges[ip][it] > fCharges[ipad][itime] / ratio) { merge = 1; break; }
+	  }
+	  //if (valOk) break;
+	  if (!merge) break;
+	}
+      }
+      //if (!valOk) fFlags[ipad][itime] = -fFlags[ipad][itime]; // not deep enough valley
+      if (merge) fFlags[ipad][itime] = -fFlags[ipad][itime]; // not deep enough valley
+    }
+  }
+
+  // Remove failed peaks
+  multimap<Double_t,Int_t>::iterator it = localMax.begin();
+  //for ( ; it != localMax.end(); ++it) cout << it->second << " ";
+  //cout << endl;
+
+  it = localMax.begin();
+  for ( ; it != localMax.end(); ++it) {
+    Int_t idig = it->second;
+    Int_t ipad = clus->Col(idig);
+    Int_t itime = clus->Bkt(idig);
+    if (fFlags[ipad][itime] > 0) continue;
+    //cout << " Before: " << idig << " " << itime << " " << ipad << " " << it->first << ", ";
+    localMax.erase(it);
+    //cout << " After: " << it->first << endl;
+  }
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::CorrectReco(TVector3 &p3loc, TVector3 &p3errCor, Int_t nPads, Double_t adc)
+{
+  // Correct reconstructed coordinates
+
+  Double_t xsec = (p3loc.Y() + fSecGeo->GetMinY()) * TMath::Tan(fSecGeo->Dphi()/2);
+  Double_t xloc = p3loc.X(), xloc0 = xloc, edge = 0.0; // distance to sector boundary
+  if (xloc < 0) edge = xloc + xsec;
+  else edge = xloc - xsec;
+  if (TMath::Abs(edge) > 1.5 || nPads > 3) return; // no corrections
+  
+  if (nPads == 1) {
+    /*
+    xloc -= TMath::Sign(0.520,edge);
+    Double_t corr = -0.15;
+    if (adc < 50000) corr = 0.206 - 2.532e-5 * adc + 8.715e-10 * adc * adc
+      - 1.610e-14 * adc * adc * adc + 1.193e-19 * adc * adc * adc * adc;
+    p3errCor[0] = 0.116;
+    */
+    Double_t corr = 0.092;
+    if (edge < 0) corr = -corr;
+    xloc -= corr;
+    p3errCor[0] = 0.386;
+  } else if (nPads == 2) {
+    /*
+    xloc -= TMath::Sign(0.200,edge);
+    Double_t corr = -0.07;
+    if (adc < 200000) corr = 0.374 - 1.046e-5 * adc + 9.595e-11 * adc * adc
+	- 4.098e-16 * adc * adc * adc + 6.811e-22 * adc * adc * adc * adc;
+    p3errCor[0] = 0.110;
+    */
+    Double_t corr = 0.002;
+    if (edge < 0) corr = -corr;
+    xloc -= corr;
+    p3errCor[0] = 0.03;
+  } else if (nPads > 2) {
+    /*
+    xloc -= TMath::Sign(0.033,edge);
+    Double_t corr = 0.01;
+    if (adc < 200000) corr = 0.188 - 4.007e-6 * adc + 3.364e-11 * adc * adc
+	- 1.265e-16 * adc * adc * adc + 1.771e-22 * adc * adc * adc * adc;
+    p3errCor[0] = 0.073;
+    */
+    Double_t corr = 0.004;
+    if (edge < 0) corr = -corr;
+    xloc -= corr;
+    p3errCor[0] = 0.04;
+  } else return;  
+  if (TMath::Abs(xloc) > xsec) xloc = TMath::Sign(xsec,xloc0);
+  p3loc.SetX(xloc);
+}
+
+//__________________________________________________________________________
+void MpdTpcClusterFinderMlem::Mlem(Int_t iclus, multimap<Double_t,Int_t> &localMax)
+{
+  // Run MLEM procedure
+
+  const Int_t nDigMax = 200; // max number of digits to apply pixel reduction
+
+  if (gROOT->FindObject("hMlem0")) gROOT->FindObject("hMlem0")->Delete();
+  if (gROOT->FindObject("hMlem1")) gROOT->FindObject("hMlem1")->Delete();
+  if (gROOT->FindObject("hTimePad")) gROOT->FindObject("hTimePad")->Delete();
+  //TCanvas *c = (TCanvas*) gROOT->FindObject("c");
+
+  vector<pixel> bins, pixels[2];
+  vector<pixel>::iterator it;
+
+  MpdTpc2dCluster* clus = (MpdTpc2dCluster*) fClusArray->UncheckedAt(iclus);
+  Int_t nDigis = clus->NDigits();
+  Int_t nx = clus->GetNumTimeBins();
+  Int_t ny = clus->GetNumPads() + 2;
+
+  TH2D *hXY = new TH2D("hTimePad","Pad No. vs Time bin",nx,clus->MinBkt(),clus->MaxBkt()+1,
+		       ny,clus->MinCol()-1,clus->MaxCol()+2);
+  TH2D *hOvfw = NULL;
+  if (clus->Overflows() > 1 && nDigis < 50) {
+    // Special treatment for (relatively) small clusters with many overflows
+    if (gROOT->FindObject("hOvfw")) gROOT->FindObject("hOvfw")->Delete();
+    hOvfw = (TH2D*) hXY->Clone("hOvfw");
+  }
+
+  // Check for edge effect - maximum digit at the edge
+  Int_t edge = 0;
+  Int_t idig = localMax.rbegin()->second;
+  Int_t ipad = clus->Col(idig);
+  if (ipad-2 == 0 || ipad-2 == 2*fSecGeo->NPadsInRows()[clus->Row()] - 1) edge = 1; // extra shift
+
+  for (Int_t idig = 0; idig < nDigis; ++idig) {
+    Int_t ipad = clus->Col(idig);
+    Int_t itime = clus->Bkt(idig);
+    //fCharges[ipad][itime] = clus->Adc(idig);
+    //fFlags[ipad][itime] = 1;
+    //fDigis[ipad][itime] = idig;
+    if (fCharges[ipad][itime] < 0.1) continue;
+    pixel pix;
+    pix.ix = hXY->GetXaxis()->FindBin(itime+0.1);
+    pix.iy = hXY->GetYaxis()->FindBin(ipad+0.1);
+    pix.qq = fCharges[ipad][itime];
+    pix.vis = 0;
+    //if (pix.qq < fgkOvfw) bins.push_back(pix);
+    bins.push_back(pix);
+    pixels[1].push_back(pix);
+    pixels[0].push_back(pix);
+    if (hOvfw && fCharges[ipad][itime] > fgkOvfw - 1) hOvfw->Fill(itime+0.1,ipad+0.1,fCharges[ipad][itime]);
+    // Consider edge effect
+    //*
+    if (edge && (ipad-2 == 0 || ipad-2 == 2*fSecGeo->NPadsInRows()[clus->Row()] - 1)) { // extra shift
+      if (ipad-2 == 0) {
+	--pix.iy;
+	fDigis[ipad-1][itime] = fDigis[ipad][itime];
+      } else { 
+	++pix.iy;
+	fDigis[ipad+1][itime] = fDigis[ipad][itime];
+      }
+      if (hXY->GetBinContent(pix.ix,pix.iy) > 0.1) continue;
+      pixels[1].push_back(pix);
+      pixels[0].push_back(pix);
+      //cout << " Edge: " << clus->Row() << " " << clus->GetId() << " " << ipad << endl; 
+    }
+    //*/
+  }
+  // Get parameters of the response function
+  Double_t sigt, sigp, correl;
+  GetResponse(clus, hXY, hOvfw, sigt, sigp, correl);
+
+  Int_t nbins = bins.size();
+  //sort (pixels[1].begin(), pixels[1].end(), SortPix);
+  //if (pixels[1].size() > nbins) pixels[1].erase(pixels[1].begin()+nbins,pixels[1].end());
+  //for (Int_t ibin = 0; ibin < nbins; ++ibin) cout << ibin << " " << pixels[1][ibin].qq << " " 
+  //						  << pixels[1][ibin].ix << " " << pixels[1][ibin].iy << endl;
+
+  //TH2D *hMlem[2] = {NULL, (TH2D*)hXY->Clone("hMlem1")};
+  TH2D *hMlem[2] = {(TH2D*)hXY->Clone("hMlem0"), (TH2D*)hXY->Clone("hMlem1")};
+  //TH2D *hExp = (TH2D*) hXY->Clone("hExp"); // for testing
+
+  // Decrease pixel size
+  //const Int_t ndiv = 5, ndiv2 = ndiv * ndiv, nloops = 2; // 
+  const Int_t ndiv = 3, ndiv2 = ndiv * ndiv, nloops = 2; // 
+  Int_t icur = 0;
+  TMatrixD cij(2,2);
+
+  for (Int_t iloop = 0; iloop < nloops; ++iloop) {
+    if (iloop) {
+      if (nDigis > nDigMax) continue;
+      nx *= ndiv; 
+      ny *= ndiv; 
+    } else if (nDigis <= nDigMax) continue; // skip first iteration
+    Int_t iprev = (iloop + 1) % 2;
+    icur = iloop % 2;
+    TString hname = "hMlem";
+    hname += icur;
+    if (hMlem[icur]) hMlem[icur]->Delete();
+    hMlem[icur] = new TH2D (hname,"",nx,hXY->GetXaxis()->GetXmin(),hXY->GetXaxis()->GetXmax(),
+			    ny,hXY->GetYaxis()->GetXmin(),hXY->GetYaxis()->GetXmax());
+    pixels[icur].clear();
+    Int_t npix = 0;
+    if (iloop) cij.ResizeTo(pixels[iprev].size()*ndiv2,nbins);
+    else cij.ResizeTo(pixels[iprev].size(),nbins);
+
+    Double_t visMax = 0.0;
+    //sort (pixels[iprev].begin(), pixels[iprev].end(), SortPix);
+
+    for (it = pixels[iprev].begin(); it != pixels[iprev].end(); ++it) {
+      Double_t xc = hMlem[iprev]->GetXaxis()->GetBinCenter(it->ix);
+      Double_t yc = hMlem[iprev]->GetYaxis()->GetBinCenter(it->iy);
+      Double_t xmin = hMlem[iprev]->GetXaxis()->GetBinLowEdge(it->ix);
+      Double_t ymin = hMlem[iprev]->GetYaxis()->GetBinLowEdge(it->iy);
+
+      for (Int_t ij = 0; ij < ndiv2; ++ij) {
+	if (iloop == 0 && ij) break;
+	Double_t x0 = xc;
+	Double_t y0 = yc;
+	pixel pix;
+	if (iloop == 0) {
+	  pix.ix = it->ix;
+	  pix.iy = it->iy;
+	} else {
+	  x0 = xmin + hMlem[icur]->GetXaxis()->GetBinWidth(1) * (ij % ndiv + 0.5);
+	  y0 = ymin + hMlem[icur]->GetYaxis()->GetBinWidth(1) * (ij / ndiv + 0.5);
+	  pix.ix = hMlem[icur]->GetXaxis()->FindBin(x0);
+	  pix.iy = hMlem[icur]->GetYaxis()->FindBin(y0);
+	  //cout << xc << " " << yc << " " << x0 << " " << y0 << endl;
+	}
+	pix.qq = 1.0;
+	//if (npix == 0) pix.qq = 9746;
+	pix.vis = 0.0;
+	pixels[icur].push_back(pix);
+
+	// Compute couplings for this pixel
+	for (Int_t ibin = 0; ibin < nbins; ++ibin) {
+	  Double_t x1 = hXY->GetXaxis()->GetBinCenter(bins[ibin].ix);
+	  Double_t y1 = hXY->GetYaxis()->GetBinCenter(bins[ibin].iy);
+	  cij(npix,ibin) = GetCij(x0, y0, x1, y1, sigt, sigp, correl);
+	  //?? pixels[icur][npix].vis += cij(npix,ibin); // visibility
+	  if (bins[ibin].qq < fgkOvfw - 1.5) pixels[icur][npix].vis += cij(npix,ibin); // visibility
+	}
+	visMax = TMath::Max(visMax,pixels[icur][npix].vis);
+
+	//cout << npix << " " << pixels[icur][npix].vis << endl;
+	++npix;
+	//if (npix >= nbins) break;
+      } // for (Int_t ij = 0;
+      //if (npix >= nbins) break;
+    } // for (it = pixels[iprev].begin();
+
+    //for (Int_t ipix = 0; ipix < npix; ++ipix) cout << ipix << " " << pixels[icur][ipix].vis << " " << pixels[icur][ipix].qq << " " << pixels[icur][ipix].ix << " " <<  pixels[icur][ipix].iy << endl;
+
+    // MLEM procedure
+    Int_t niter = 10;
+    for (Int_t iter = 0; iter < niter; ++iter) {
+
+      // Calculate expectations for all pads
+      for (Int_t ibin = 0; ibin < nbins; ++ibin) {
+	bins[ibin].vis = 0.0;
+	for (Int_t i = 0; i < npix; ++i) bins[ibin].vis += (pixels[icur][i].qq * cij(i,ibin)); // bins[ibin].vis - just storage
+	//?? bins[ibin].vis = TMath::Min (bins[ibin].vis, fglOvfw);
+      }
+
+      // Maximization
+      for (Int_t ipix = 0; ipix < npix; ++ipix) {
+	Double_t sum = 0.0;
+
+	for (Int_t ibin = 0; ibin < nbins; ++ibin) {
+	  if (bins[ibin].vis > 1.e-6) sum += (bins[ibin].qq / bins[ibin].vis * cij(ipix,ibin));
+	}
+
+	if (pixels[icur][ipix].vis > 1.e-6) pixels[icur][ipix].qq *= (sum / pixels[icur][ipix].vis);
+	//if (pixels[icur][ipix].vis > 1.e-6) pixels[icur][ipix].qq *= (sum / visMax);
+      }
+    } // for (Int_t iter = 0; 
+
+    // Visual control
+    /*
+    if (iloop == nloops - 1) {
+      for (Int_t ipix = 0; ipix < npix; ++ipix) 
+	hMlem[icur]->SetBinContent(pixels[icur][ipix].ix,pixels[icur][ipix].iy,pixels[icur][ipix].qq);
+      //c->cd(3);
+      hMlem[icur]->Draw("col");
+    }
+    */
+    /*
+    hExp->Reset();
+    for (Int_t ipix = 0; ipix < nbins; ++ipix) 
+      hExp->SetBinContent(bins[ipix].ix,bins[ipix].iy,bins[ipix].vis);
+    */
+  } // for (Int_t iloop = 0; 
+
+  //
+  // Find local maxima
+  //
+
+  vector<Double_t> tmp;
+  vector<vector<Double_t> > charges;
+  tmp.assign(nx,0);
+  charges.assign(ny,tmp);
+  vector<Int_t> itmp;
+  vector<vector<Int_t> > flags;
+  itmp.assign(nx,0);
+  flags.assign(ny,itmp);
+  localMax.clear();
+  Int_t npix = pixels[icur].size();
+
+  for (Int_t ipix = 0; ipix < npix; ++ipix) {
+    Int_t ipad = pixels[icur][ipix].iy - 1;
+    Int_t itime = pixels[icur][ipix].ix - 1;
+    charges[ipad][itime] = pixels[icur][ipix].qq;
+    flags[ipad][itime] = ipix + 1;
+  }
+
+  // Exclude pads which are not local maxima
+  for (Int_t ipix = 0; ipix < npix; ++ipix) {
+    Int_t ipad = pixels[icur][ipix].iy - 1;
+    Int_t itime = pixels[icur][ipix].ix - 1;
+    Int_t iok = 1;
+
+    for (Int_t ip = -1; ip < 2; ++ip) {
+      for (Int_t it = -1; it < 2; ++it) {
+	if (TMath::Abs(it) == TMath::Abs(ip)) continue; // exclude diagonals
+	if (it == 0 && ip == 0) continue;
+	Int_t ip1 = ipad + ip, it1 = itime + it;
+	if (ip1 < 0 || ip1 >= ny) continue;
+	if (it1 < 0 || it1 >= nx) continue;
+	if (flags[ip1][it1] == 0) continue;
+	//if (pixels[icur][ipix].qq < charges[ip1][it1]) { flags[ipad][itime] = -1; break; }
+	//else if (pixels[icur][ipix].qq == charges[ip1][it1] && flags[ip1][it1] > 0) { flags[ipad][itime] = -1; break; }
+	//if (flags[ip1][it1] <= 0) continue;
+	if (pixels[icur][ipix].qq < charges[ip1][it1]) { flags[ipad][itime] *= -1; iok = 0; break; }
+	else if (pixels[icur][ipix].qq == charges[ip1][it1] && flags[ip1][it1] > 0) { flags[ipad][itime] *= -1; iok = 0; break; }
+      }
+      if (!iok) break;
+    }
+  }
+
+  for (Int_t ipix = 0; ipix < npix; ++ipix) {
+    Int_t ipad = pixels[icur][ipix].iy - 1;
+    Int_t itime = pixels[icur][ipix].ix - 1;
+    if (flags[ipad][itime] <= 0) continue;
+    localMax.insert(pair<Double_t,Int_t>(pixels[icur][ipix].qq,ipix));
+  }
+
+  // Apply Peak-and-valley procedure in pixel domain
+  if (localMax.size() > 1) PeakAndValley(pixels[icur], localMax, charges, flags);
+
+  // Create hits
+  Int_t nHits0 = fHitArray->GetEntriesFast();
+  vector<multimap<Double_t,Int_t> > pixInMax;
+  multimap<Double_t,Int_t> mtmp;
+  pixInMax.assign(localMax.size(),mtmp);
+  CreateHits(pixels[icur], localMax, charges, flags, iclus, pixInMax);
+
+  // Get correct charge 
+  ChargeMlem(nHits0, pixels[icur], bins, pixInMax, cij);
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::PeakAndValley(const vector<pixel> &pixels, multimap<Double_t,Int_t> &localMax, 
+					    vector<vector<Double_t> > &charges, vector<vector<Int_t> > &flags)
+{
+  // Apply peak-and-valley cuts to remove some local maxima in pixel domain
+
+  const Double_t ratio = 1.2;
+  multimap<Double_t,Int_t>::reverse_iterator rit = localMax.rbegin(), rit1;
+  ++rit;
+
+  for ( ; rit != localMax.rend(); ++rit) {
+    Int_t ipix = rit->second;
+    Int_t ipad = pixels[ipix].iy - 1;
+    Int_t itime = pixels[ipix].ix - 1;
+    if (flags[ipad][itime] <= 0) continue; // merged local max
+
+    // Loop over higher peaks
+    rit1 = localMax.rbegin();
+    for ( ; rit1 != rit; ++rit1) {
+      Int_t ipix0 = rit1->second;
+      Int_t ipad0 = pixels[ipix0].iy - 1;
+      Int_t itime0 = pixels[ipix0].ix - 1;
+      if (flags[ipad0][itime0] <= 0) continue; // merged local max
+
+      Int_t dpad = ipad - ipad0, dt = itime - itime0;
+      Int_t i0 = itime0, i1 = itime, j0 = ipad0, j1 = ipad, intime = 1;
+      if (TMath::Abs(dpad) > TMath::Abs(dt)) i0 = ipad0, i1 = ipad, j0 = itime0, j1 = itime, intime = 0;
+      Int_t stepi = TMath::Sign(1,i1-i0), stepj = TMath::Sign(1,j1-j0);
+      
+      //Int_t valOk = 0;
+      Int_t merge = 0;
+      //if (TMath::Abs(dpad) <= 1 && TMath::Abs(dt) <= 1) merge = 1;
+      //else {
+      if (TMath::Abs(dpad) <= 1 && TMath::Abs(dt) <= 1) {
+	if (charges[ipad-dpad][itime] > charges[ipad][itime] / ratio ||
+	    charges[ipad][itime-dt] > charges[ipad][itime] / ratio) merge = 1;
+      } else {
+	for (Int_t ii = i0 + stepi; ii != i1; ii += stepi) {
+	  merge = 0;
+	  for (Int_t jj = j0; jj != j1 + stepj; jj += stepj) {
+	    if (TMath::Abs(jj-j0) > TMath::Abs(ii-i0)) continue;
+	    if (TMath::Abs(jj-j1) > TMath::Abs(ii-i1)) continue;
+	    Int_t ip = ii, it = jj;
+	    if (intime) ip = jj, it = ii;
+	    //if (fCharges[ip][it] < fCharges[ipad][itime] / ratio) { valOk = 1; break; }
+	    if (charges[ip][it] > charges[ipad][itime] / ratio) { merge = 1; break; }
+	  }
+	  //if (valOk) break;
+	  if (!merge) break;
+	}
+      }
+      //if (!valOk) fFlags[ipad][itime] = -fFlags[ipad][itime]; // not deep enough valley
+      if (merge) flags[ipad][itime] = -flags[ipad][itime]; // not deep enough valley
+    }
+  }
+
+  // Remove failed peaks
+  multimap<Double_t,Int_t>::iterator it = localMax.begin();
+  //for ( ; it != localMax.end(); ++it) cout << it->second << " ";
+  //cout << endl;
+
+  it = localMax.begin();
+  for ( ; it != localMax.end(); ++it) {
+    Int_t ipix = it->second;
+    Int_t ipad = pixels[ipix].iy - 1;
+    Int_t itime = pixels[ipix].ix - 1;
+    //cout << " Before: " << ipix << " " << itime << " " << ipad << " " << it->first << endl;
+    if (flags[ipad][itime] > 0) continue;
+    //cout << " Before: " << idig << " " << itime << " " << ipad << " " << it->first << ", ";
+    localMax.erase(it);
+    //cout << " After: " << it->first << endl;
+  }
+}
+
+//__________________________________________________________________________
+
+//static Bool_t MpdTpcClusterFinderMlem::SortPix(const pixel i, const pixel j)
+static Bool_t SortPix(const MpdTpcClusterFinderMlem::pixel i, const MpdTpcClusterFinderMlem::pixel j)
+{
+  // Sorting function
+
+  return i.qq > j.qq;
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::GetResponse(const MpdTpc2dCluster* clus, TH2D *hXY, TH2D *hOvfw, 
+					  Double_t &sigt, Double_t &sigp, Double_t &correl)
+{
+  // Get response parameters
+
+  const Double_t sigx[2] = {0.584, 0.597};
+  const Double_t coef[2][5] = {{1.10951, -1.12393e-3, 1.62066e-4, -4.40694e-6, 6.26894e-8},
+			       {1.11737, 1.16204e-3, -1.01821e-4, 3.83333e-6, 0.0}};
+  
+  // Dip angle estimate
+  Double_t padMean = (hXY->GetYaxis()->GetXmin() + hXY->GetYaxis()->GetXmax()) / 2;
+  Double_t timeMean = (hXY->GetXaxis()->GetXmin() + hXY->GetXaxis()->GetXmax()) / 2;
+  Double_t xloc = fSecGeo->Pad2Xloc(padMean,clus->Row());
+  Int_t padID = fSecGeo->PadID(clus->GetSect() % fSecGeo->NofSectors(), clus->Row());
+  Double_t yloc = fSecGeo->LocalPadPosition(padID).Y();
+  Double_t zloc = fSecGeo->TimeBin2Z(timeMean);
+  TVector3 p3loc(xloc, yloc, zloc), p3glob;
+  if (clus->GetSect() >= fSecGeo->NofSectors()) p3loc[2] = -p3loc[2];
+  fSecGeo->Local2Global(clus->GetSect(), p3loc, p3glob);
+  Double_t dip = TMath::ATan2 (TMath::Abs(p3glob[2]),p3glob.Pt()) * TMath::RadToDeg();
+  dip = TMath::Min (80.0,TMath::Abs(dip));
+
+  Int_t ireg = (clus->Row() < 27) ? 0 : 1; // pad height region
+  Double_t dip2 = dip * dip;
+  Double_t dip3 = dip2 * dip;
+  Double_t dip4 = dip3 * dip;
+  sigt = coef[ireg][0] + coef[ireg][1] * dip + coef[ireg][2] * dip2 + coef[ireg][3] * dip3 
+    + coef[ireg][4] * dip4;
+  sigp = sigx[ireg];
+  correl = 0.0;
+  if (hOvfw) {
+    correl = hOvfw->GetCorrelationFactor();
+    //sigt = hOvfw->GetRMS(1);
+    //sigp = hOvfw->GetRMS(2);
+  }
+}
+
+//__________________________________________________________________________
+
+Double_t MpdTpcClusterFinderMlem::GetCij(Double_t x0, Double_t y0, Double_t x1, Double_t y1, 
+					 Double_t sigt, Double_t sigp, Double_t correl)
+{
+  // Compute couplings - Gauss parameter Sigma_t as a function of the dip angle
+
+  Double_t dx = x1 - x0;
+  Double_t dy = y1 - y0;
+  dx /= sigt;
+  dy /= sigp; 
+
+  if (TMath::Abs(correl) < 0.001) return TMath::Exp(-dx*dx/2 - dy*dy/2) / 2 / sigt / sigp / TMath::Pi();
+  else {
+    Double_t corr2 = 1 - correl * correl;
+    return TMath::Exp((-dx*dx/2 + correl*dx*dy - dy*dy/2)/corr2) / 2 / sigt / sigp / TMath::Pi() / TMath::Sqrt(corr2);
+  }
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::CreateHits(const vector<pixel> &pixels, multimap<Double_t,Int_t> &localMax, 
+					 vector<vector<Double_t> > &charges, vector<vector<Int_t> > &flags,
+					 Int_t iclus, vector<multimap<Double_t,Int_t> > &pixInMax)
+{
+  // Create hits from pixels 
+
+  Int_t nLocMax0 = localMax.size();
+  MpdTpc2dCluster* clus = (MpdTpc2dCluster*) fClusArray->UncheckedAt(iclus);
+  TH2D *hXY = (TH2D*) gROOT->FindObject("hTimePad");
+  TH2D *hMlem = (TH2D*) gROOT->FindObject("hMlem1");
+  //if (hMlem == NULL) hMlem = (TH2D*) gROOT->FindObject("hMlem1");
+  Double_t scale = hXY->GetBinWidth(1) / hMlem->GetBinWidth(1);
+
+  multimap<Double_t,Int_t>::reverse_iterator rit = localMax.rbegin();
+  //vector<Int_t> vecDig;
+  map<Int_t,Double_t> mapIdQ;
+  TVector3 p3loc, p3glob, p3err(0.05,0.0,0.1);
+  Int_t ihit = fHitArray->GetEntriesFast(), nHitsAdd = 0;
+
+  for ( ; rit != localMax.rend(); ++rit) {
+    Int_t ipix = rit->second;
+    Int_t ipad = pixels[ipix].iy - 1;
+    Int_t itime = pixels[ipix].ix - 1;
+    if (flags[ipad][itime] <= 0) continue; // merged local max
+    //vecDig.clear();
+    mapIdQ.clear();
+
+    set<pair<Int_t,Int_t> > selPix;
+    Double_t padMean = 0, timeMean = 0, adcTot = 0, sum2t = 0, sum2p = 0;
+
+    // Process complex cluster - start from maximum and go outward (up to 5 steps), 
+    // adding pixels with respectively lower charges
+    for (Int_t idirp = -1; idirp < 2; idirp += 2) {
+      for (Int_t ip = 0; ip < 5; ++ip) {
+      //for (Int_t ip = 0; ip < 8; ++ip) {
+	if (idirp > 0 && ip == 0) continue;
+	Int_t ipsign = ip * idirp;
+	Int_t ip1 = ipad + ipsign;
+	if (ip1 < 0 || ip1 >= flags.size()) break;
+	
+	for (Int_t idirt = -1; idirt < 2; idirt += 2) {
+	  for (Int_t it = 0; it < 5; ++it) {
+	    if (idirt > 0 && it == 0) continue;
+	    Int_t itsign = it * idirt;
+	    
+	    Int_t it1 = itime + itsign;
+	    if (it1 < 0 || it1 >= (flags[0]).size()) break;
+	    if (flags[ip1][it1] == 0) continue;
+	    
+	    Int_t add = 1;
+	    if (ip || it) {
+	      if (flags[ip1][it1] > 0) continue; // case when 2 local max next to each other on 1 diagonal 
+	      // Check gradient
+	      add = 0;
+	      Int_t ipprev = ipsign, itprev = itsign;
+	      if (it) {
+		itprev -= idirt;
+		Int_t it10 = itime + itprev;
+		if (it10 >= 0 && it10 < (flags[0]).size() && flags[ip1][it10] != 0) {
+		  if (selPix.find(pair<Int_t,Int_t>(ip1,it10)) != selPix.end() 
+		      && charges[ip1][it1] <= charges[ip1][it10]) add = 1;
+		}
+	      }
+	      if (add == 0 && ip) {
+		ipprev -= idirp;
+		Int_t ip10 = ipad + ipprev;
+		if (ip10 >= 0 && ip10 < flags.size() && flags[ip10][it1] != 0) {
+		  if (selPix.find(pair<Int_t,Int_t>(ip10,it1)) != selPix.end() 
+		      && charges[ip1][it1] <= charges[ip10][it1]) add = 1;
+		}
+	      }
+	    }
+		
+	    if (!add) break;
+	    padMean += ip1 * charges[ip1][it1];
+	    timeMean += it1 * charges[ip1][it1];
+	    adcTot += charges[ip1][it1];
+	    //vecDig.push_back(fDigis[ip1][it1]);
+	    Int_t ipBin = hXY->GetYaxis()->FindBin(hMlem->GetYaxis()->GetBinCenter(ip1+1)); // original bin number
+	    Int_t itBin = hXY->GetXaxis()->FindBin(hMlem->GetXaxis()->GetBinCenter(it1+1)); // original bin number
+	    ipBin = TMath::Max (TMath::Nint (hXY->GetYaxis()->GetBinLowEdge(ipBin)), 0);
+	    itBin = TMath::Nint (hXY->GetXaxis()->GetBinLowEdge(itBin));
+	    Int_t id = clus->Sec(fDigis[ipBin][itBin]);
+	    //if (mapIdQ.find(id) == mapIdQ.end()) mapIdQ[id] = fCharges[ip1][it1];
+	    //else mapIdQ[id] = mapIdQ[id] + fCharges[ip1][it1];
+	    //else mapIdQ[id] = TMath::Max (mapIdQ[id], fCharges[ip1][it1]);
+	    if (mapIdQ.find(id) == mapIdQ.end()) mapIdQ[id] = 1;
+	    else mapIdQ[id] = mapIdQ[id] + 1; // number of digits with the same ID
+	    selPix.insert(pair<Int_t,Int_t>(ip1,it1));
+
+	    sum2t += it1 * it1 * charges[ip1][it1];
+	    sum2p += ip1 * ip1 * charges[ip1][it1];
+
+	    pixInMax[nHitsAdd].insert(pair<Double_t,Int_t>(-charges[ip1][it1],flags[ip1][it1]));
+	  }
+	}
+      }
+    }
+
+    //padMean = (padMean / adcTot + 0.5) / scale;
+    padMean = (padMean / adcTot + 0.5) / scale - 2; // coorrect for shift
+    timeMean = (timeMean / adcTot + 0.5) / scale;
+
+    Double_t xloc = fSecGeo->Pad2Xloc(padMean-0.5+hMlem->GetYaxis()->GetXmin(),clus->Row());
+    Int_t padID = fSecGeo->PadID(clus->GetSect() % fSecGeo->NofSectors(), clus->Row());
+    Double_t yloc = fSecGeo->LocalPadPosition(padID).Y();
+    Double_t zloc = fSecGeo->TimeBin2Z(timeMean-0.5+hMlem->GetXaxis()->GetXmin());
+    p3loc.SetXYZ(xloc, yloc, zloc);
+    Double_t rmsZ = TMath::Sqrt (sum2t/adcTot/scale/scale - timeMean*timeMean);
+    Double_t rmsX = TMath::Sqrt (sum2p/adcTot/scale/scale - padMean*padMean);
+    //p3err[1] = fSecGeo->TimeBin2Z(rms); // to pass the value
+    //p3err[1] = rms;
+    //cout << " Result: " << nLocMax0 << " " << pixels.size() << " " << xloc << " " << zloc << endl;
+    
+    // Apply corrections
+    TVector3 p3errCor(p3err);
+    CorrectRecoMlem(p3loc, p3errCor, clus, adcTot);
+    
+    if (clus->GetSect() >= fSecGeo->NofSectors()) p3loc[2] = -p3loc[2];
+    fSecGeo->Local2Global(clus->GetSect(), p3loc, p3glob);
+    
+    // Dip angle correction (for interaction point with Z = 0)
+    Double_t dip = TMath::ATan2 (TMath::Abs(p3glob[2]),p3glob.Pt()) * TMath::RadToDeg();
+    p3errCor[2] = TMath::Max (p3errCor[2], 0.116 - 0.0046 * dip + 0.00015 * dip * dip);
+    p3errCor[2] = TMath::Min (p3errCor[2], 0.5);
+    // Correct Z-coordinate for rows = 0 and 27
+    //if ((clus->Row() == 0 || clus->Row() == 27) && rmsZ > 1.0) {
+    if (clus->Row() == 0 && rmsZ > 1.0) {
+      Double_t zcor = 0;
+      if (clus->Row() == 0) zcor = -0.011 + 0.002 * dip;
+      else zcor = -0.029 + 0.005 * dip + 3.886e-5 * dip * dip;
+      zcor = TMath::Max (zcor, 0.);
+      zcor = TMath::Min (zcor, 0.6);
+      p3loc[2] -= TMath::Sign (zcor, p3loc[2]);
+      p3glob[2] -= TMath::Sign (zcor, p3glob[2]);
+    }
+    
+    MpdTpcHit* hit = new ((*fHitArray)[ihit++]) MpdTpcHit(padID, p3glob, p3errCor, iclus); 
+    hit->SetLayer(clus->Row());
+    hit->SetLocalPosition(p3loc); // point position
+    hit->SetEnergyLoss(adcTot);
+    Int_t ireg = (clus->Row() < fSecGeo->NofRowsReg(0)) ? 0 : 1;
+    Double_t step = fSecGeo->PadHeight(ireg);
+    hit->SetStep(step);
+    hit->SetModular(1); // modular geometry flag
+    hit->SetPad(Int_t(padMean-0.5+hMlem->GetYaxis()->GetXmin()));
+    hit->SetBin(Int_t(timeMean-0.5+hMlem->GetXaxis()->GetXmin()));
+    hit->SetRMS(rmsX, 0);
+    hit->SetRMS(rmsZ, 1);
+    hit->SetNdigits(-selPix.size()); // negative value
+    ++nHitsAdd;
+    
+    // !!! Warning: FairLinks are not persistent !!! 
+    //hit->AddLink(FairLink(MpdTpcHit::PointIndex, pointIndx));
+    vector<Int_t> vecDig;
+    // Store maximum 3 track IDs with the highest charge contribution
+    multimap<Double_t,Int_t> mapDig;
+    for (map<Int_t,Double_t>::iterator mit = mapIdQ.begin(); mit != mapIdQ.end(); ++mit) 
+      mapDig.insert(pair<Double_t,Int_t>(-mit->second,mit->first));
+    multimap<Double_t,Int_t>::iterator mit = mapDig.begin();
+    while (mit != mapDig.end() && vecDig.size() < 9) {
+      //while (mit != mapDig.end() && vecDig.size() < 1) {
+      vecDig.push_back(mit->second);
+      ++mit;
+    }
+    Int_t ndig = vecDig.size();
+    /*
+      for (Int_t idig = 0; idig < ndig; ++idig)
+      hit->AddLink(FairLink(MpdTpcHit::MCTrackIndex, clus->Sec(vecDig[idig]))); // trackID stored in Sec
+    */
+    for (Int_t idig = 0; idig < ndig; ++idig) {
+      hit->AddLink(FairLink(MpdTpcHit::MCTrackIndex, vecDig[idig], idig)); // weight = idig
+      hit->AddID(vecDig[idig]);
+    }
+    //cout << hit->GetNLinks() << " " << *(vecDig.begin()) << " " << hit->GetLinksWithType(MpdTpcHit::MCTrackIndex).GetLink(0).GetIndex() << " " << hit->GetLinksWithType(MpdTpcHit::MCTrackIndex).GetLink(hit->GetNLinks()-1).GetIndex() << endl;
+  } // for ( ; rit != localMax.rend();
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::CorrectRecoMlem(TVector3 &p3loc, TVector3 &p3errCor, MpdTpc2dCluster *clus, Double_t adc)
+{
+  // Correct reconstructed coordinates
+
+  Double_t xsec = (p3loc.Y() + fSecGeo->GetMinY()) * TMath::Tan(fSecGeo->Dphi()/2);
+  Double_t xloc = p3loc.X(), xloc0 = xloc, edge = 0.0; // distance to sector boundary
+  if (xloc < 0) edge = xloc + xsec;
+  else edge = xloc - xsec;
+  if (TMath::Abs(edge) > 1.1 || adc > 2000) return; // no corrections
+  
+  Double_t adc2 = adc * adc;
+  Double_t adc3 = adc2 * adc;
+  if (clus->Edge()) {
+    // Max pad at the edge
+    Double_t corr = 3.886 - 0.007 * adc + 3.371e-6 * adc2 - 8.175e-11 * adc3 - 1.812e-13 * adc3 * adc;
+    corr /= 10; // cm
+    if (edge < 0) corr = -corr;
+    xloc -= corr;
+    p3errCor[0] = 0.15;
+  } else {
+    if (adc > 1000) return;
+    Double_t corr = -0.22 / 10; // cm
+    if (adc < 700) corr = 7.521 - 0.012 * adc - 9.085e-6 * adc2 + 1.365e-8 * adc3; 
+    corr /= 10; // cm
+    if (edge < 0) corr = -corr;
+    xloc -= corr;
+    p3errCor[0] = 0.2;
+  }
+  if (TMath::Abs(xloc) > xsec) xloc = TMath::Sign(xsec,xloc0);
+  p3loc.SetX(xloc);
+}
+
+//__________________________________________________________________________
+
+void MpdTpcClusterFinderMlem::ChargeMlem(Int_t nHits0, vector<pixel> &pixels, vector<pixel> &bins, 
+					 vector<multimap<Double_t,Int_t> > &pixInMax, const TMatrixD &cij)
+{
+  // Get correct charge of hits after MLEM 
+  // (reduce number of pixels not to exceed number of time-pad bins)
+
+  Int_t nH = fHitArray->GetEntriesFast();
+  Double_t qTot = 0.0;
+
+  for (Int_t i = nHits0; i < nH; ++i) {
+    MpdTpcHit* hit = (MpdTpcHit*) fHitArray->UncheckedAt(i);
+    qTot += hit->GetQ();
+  }
+
+  Int_t nBinsTot = bins.size(), nBins = 0, nHits = pixInMax.size();
+  Double_t coef = nBinsTot / qTot;
+  vector<pixel> pixs;
+  multimap<Double_t,Int_t>::iterator it;
+
+  // Divide bins between hits proportionally to their charges
+  for (Int_t i = nH-1; i >= nHits0; --i) {
+    MpdTpcHit* hit = (MpdTpcHit*) fHitArray->UncheckedAt(i);
+    Int_t nb = TMath::Nint (hit->GetQ() * coef);
+    if (nb == 0) ++nb;
+    nBins += nb;
+    if (nBins > nBinsTot) nb -= (nBins - nBinsTot);
+    else if (i == nHits0 && nBins < nBinsTot) nb -= (nBins - nBinsTot);
+    Int_t jmax = i - nHits0, nsel = 0;
+
+    for (it = pixInMax[jmax].begin(); it != pixInMax[jmax].end(); ++it) {
+      ++nsel;
+      if (nsel > nb) break;
+      Int_t ipix = TMath::Abs(it->second) - 1;
+      pixs.push_back(pixels[ipix]);
+      pixs.back().qq = 1;
+      pixs.back().ix = ipix; // save pixel index
+      pixs.back().iy = i; // save hit index
+    }
+  }
+
+  // MLEM procedure
+  Int_t niter = 2, npix = pixs.size();
+  for (Int_t iter = 0; iter < niter; ++iter) {
+
+    // Calculate expectations for all pads
+    for (Int_t ibin = 0; ibin < nBinsTot; ++ibin) {
+      bins[ibin].vis = 0.0;
+      for (Int_t i = 0; i < npix; ++i) bins[ibin].vis += (pixs[i].qq * cij(pixs[i].ix,ibin)); // bins[ibin].vis - just storage
+      //?? bins[ibin].vis = TMath::Min (bins[ibin].vis, fglOvfw);
+    }
+
+    // Maximization
+    for (Int_t ipix = 0; ipix < npix; ++ipix) {
+      Double_t sum = 0.0;
+
+      for (Int_t ibin = 0; ibin < nBinsTot; ++ibin) {
+	if (bins[ibin].vis > 1.e-6) sum += (bins[ibin].qq / bins[ibin].vis * cij(pixs[ipix].ix,ibin));
+      }
+
+      if (pixs[ipix].vis > 1.e-6) pixs[ipix].qq *= (sum / pixs[ipix].vis);
+      //if (pixels[icur][ipix].vis > 1.e-6) pixels[icur][ipix].qq *= (sum / visMax);
+    }
+  } // for (Int_t iter = 0; 
+
+  // Compute hit charges
+  map<Int_t,Double_t> charges;
+  for (Int_t ipix = 0; ipix < npix; ++ipix) {
+    if (charges.find(pixs[ipix].iy) == charges.end()) charges[pixs[ipix].iy] = pixs[ipix].qq;
+    else charges[pixs[ipix].iy] += pixs[ipix].qq;
+  }
+
+  
+  for (Int_t ih = nHits0; ih < nH; ++ih) {
+    MpdTpcHit* hit = (MpdTpcHit*) fHitArray->UncheckedAt(ih);
+    //cout << hit->GetQ() << " " << charges[ih] << " " << hit->GetNdigits() << " " << hit->GetLayer() << " " << nHits << endl;
+    hit->SetEnergyLoss(charges[ih]/1.089); // coefficient due to different peak with and w/out MLEM
+  }
+}
+
+//__________________________________________________________________________
+ClassImp(MpdTpcClusterFinderMlem)
diff --git a/tpc/MpdTpcClusterFinderMlem.h b/tpc/MpdTpcClusterFinderMlem.h
new file mode 100644
index 0000000..053d5fa
--- /dev/null
+++ b/tpc/MpdTpcClusterFinderMlem.h
@@ -0,0 +1,114 @@
+//-----------------------------------------------------------
+// File and Version Information:
+// $Id$
+//
+// Description:
+//      TpcClusterFinderMlem reads in TPC digits and reconstructs clusters and hits
+//
+//
+// Environment:
+//      Software developed for the MPD Detector at NICA.
+//
+// Author List:
+//      Alexandr Zinchenko LHEP, JINR, Dubna - 11-January-2016
+//
+//-----------------------------------------------------------
+
+#ifndef MPDTPCCLUSTERFINDERMLEM_HH
+#define MPDTPCCLUSTERFINDERMLEM_HH
+
+// Base Class Headers ----------------
+#include "FairTask.h"
+#include <TMatrixD.h>
+#include <TVector3.h>
+#include <set>
+#include <vector>
+#include <map>
+
+// Collaborating Class Headers -------
+
+// Collaborating Class Declarations --
+class MpdTpc2dCluster;
+class MpdTpcSectorGeo;
+class TClonesArray;
+class TH2D;
+//class TMatrixD;
+
+class MpdTpcClusterFinderMlem : public FairTask {
+public:
+
+  // Constructors/Destructors ---------
+  MpdTpcClusterFinderMlem();
+  ~MpdTpcClusterFinderMlem();
+
+  // Operators
+  
+  // Accessors -----------------------
+
+  // Modifiers -----------------------
+  void SetPersistence(Bool_t opt = kTRUE) { fPersistence = opt; }
+
+  // Operations ----------------------
+  
+  virtual InitStatus Init();
+  void FinishTask();
+
+  virtual void Exec(Option_t* opt);
+  //virtual void Clear(Option_t* opt);
+
+  struct pixel {
+    Double_t qq;
+    Int_t ix;
+    Int_t iy;
+    Double_t vis;
+  };
+
+private:
+
+  // Private Data Members ------------
+  static const Int_t fgkNsec2 = 24; // number of readout sectors (12 * 2)
+  static const Int_t fgkNpads = 128, fgkNtimes = 512; // max number of pads and time bins
+  static const Int_t fgkOvfw = 4095; // overflow value
+
+  TClonesArray* fDigiArray;
+  TClonesArray* fClusArray;
+  TClonesArray* fHitArray;
+  //TClonesArray** fPrimArray;
+  std::set<Int_t>* fDigiSet[fgkNsec2];
+  Double_t fCharges[fgkNpads][fgkNtimes];
+  Int_t fFlags[fgkNpads][fgkNtimes];
+  Int_t fDigis[fgkNpads][fgkNtimes];
+  Bool_t fPersistence;
+  MpdTpcSectorGeo* fSecGeo;
+
+  // Private Methods -----------------
+  void ProcessPadrow(Int_t isec, Int_t irow); // process one padrow of a sector
+  void NextPixel(MpdTpc2dCluster* clus, Int_t ipad, Int_t itime); // add next pixel to the cluster
+  void FindHits(); // find hits
+  void Mlem(Int_t iclus, std::multimap<Double_t,Int_t> &localMax); // MLEM procedure
+  //bool myfunc(const pixel i, const pixel j); // sorting function
+  //static Bool_t SortPix(const pixel i, const pixel j); // sorting function
+  void GetResponse(const MpdTpc2dCluster* clus, TH2D *hXY, TH2D *hOvfw, 
+		   Double_t &sigt, Double_t &sigp, Double_t &correl); // get response parameters
+  Double_t GetCij(Double_t x0, Double_t y0, Double_t x1, Double_t y1, 
+		  Double_t sigt, Double_t sigp, Double_t correl); // compute pixel-to-bin couplings
+  void PeakAndValley(const MpdTpc2dCluster* clus, std::multimap<Double_t,Int_t> &localMax); // peak-and-valley
+  void PeakAndValley(const std::vector<pixel> &pixels, std::multimap<Double_t,Int_t> &localMax, 
+		     std::vector<std::vector<Double_t> > &charges, std::vector<std::vector<Int_t> > &flags); // peak-and-valley in pixel domain
+  void CreateHits(const std::vector<pixel> &pixels, std::multimap<Double_t,Int_t> &localMax, 
+		  std::vector<std::vector<Double_t> > &charges, std::vector<std::vector<Int_t> > &flags,
+		  Int_t iclus, std::vector<std::multimap<Double_t,Int_t> > &pixInMax); // create hits from pixels
+  void CorrectReco(TVector3 &p3loc, TVector3 &p3err, Int_t nPads, Double_t adc); // correct reco coordinates and errors
+  void CorrectRecoMlem(TVector3 &p3loc, TVector3 &p3errCor, MpdTpc2dCluster *clus, Double_t adc); // after MLEM
+  void ChargeMlem(Int_t nHits0, std::vector<pixel> &pixels, std::vector<pixel> &bins, 
+		  std::vector<std::multimap<Double_t,Int_t> > &pixInMax, const TMatrixD &cij); // correct hit charges after MLEM
+
+  ClassDef(MpdTpcClusterFinderMlem,0)
+
+};
+
+#endif
+
+//--------------------------------------------------------------
+// $Log$
+//--------------------------------------------------------------
diff --git a/tpc/tpcLinkDef.h b/tpc/tpcLinkDef.h
index 9c71ad5..990aa10 100644
--- a/tpc/tpcLinkDef.h
+++ b/tpc/tpcLinkDef.h
@@ -28,6 +28,7 @@
 #pragma link C++ class MpdTpcDigitizerAZ+;
 #pragma link C++ class MpdTpcClusterFinderAZ+;
 #pragma link C++ class MpdTPCpid+;
+#pragma link C++ class MpdTpcClusterFinderMlem+;
 
 #endif
 
-- 
GitLab