Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:20:34

0001 
0002 /**
0003  * \file TpcCentralMembraneMatching.cc
0004  * \brief match reconstructed CM clusters to CM pads, calculate differences, store on the node tree and compute distortion reconstruction maps
0005  * \author Tony Frawley <frawley@fsunuc.physics.fsu.edu>, Hugo Pereira Da Costa <hugo.pereira-da-costa@cea.fr>
0006  */
0007 
0008 #include "TpcCentralMembraneMatching.h"
0009 
0010 #include <trackbase/CMFlashDifferenceContainerv1.h>
0011 #include <trackbase/CMFlashDifferencev1.h>
0012 #include <trackbase/LaserCluster.h>
0013 #include <trackbase/LaserClusterContainerv1.h>
0014 #include <trackbase/TpcDefs.h>
0015 
0016 #include <tpc/LaserEventInfo.h>
0017 
0018 #include <ffaobjects/EventHeader.h>
0019 
0020 #include <fun4all/Fun4AllReturnCodes.h>
0021 
0022 #include <phool/PHCompositeNode.h>
0023 #include <phool/getClass.h>
0024 #include <phool/phool.h>
0025 
0026 #include <TF1.h>
0027 #include <TFile.h>
0028 #include <TGraph.h>
0029 #include <TH1.h>
0030 #include <TH2.h>
0031 #include <TStyle.h>
0032 #include <TTree.h>
0033 #include <TVector3.h>
0034 
0035 #include <algorithm>
0036 #include <cmath>
0037 #include <format>
0038 #include <iomanip>
0039 #include <set>
0040 #include <string>
0041 
0042 int layerMins[3] = {16, 23, 39};
0043 int layerMaxes[3] = {22, 38, 54};
0044 
0045 namespace
0046 {
0047   template <class T>
0048   constexpr T delta_phi(const T& phi)
0049   {
0050     if (phi > M_PI)
0051     {
0052       return phi - 2. * M_PI;
0053     }
0054     if (phi <= -M_PI)
0055     {
0056       return phi + 2. * M_PI;
0057     }
0058 
0059     return phi;
0060   }
0061 
0062   template <class T>
0063   constexpr T square(const T& x)
0064   {
0065     return x * x;
0066   }
0067 
0068   template <class T>
0069   inline T get_r(const T& x, const T& y)
0070   {
0071     return std::sqrt(square(x) + square(y));
0072   }
0073 
0074   // stream acts vector3
0075   [[maybe_unused]] std::ostream& operator<<(std::ostream& out, const Acts::Vector3& v)
0076   {
0077     out << "(" << v.x() << ", " << v.y() << ", " << v.z() << ")";
0078     return out;
0079   }
0080 
0081   /// normalize distortions based on the number of entries in each cell, as recorded in the m_hentries histogram
0082   [[maybe_unused]] void normalize_distortions(TpcDistortionCorrectionContainer* dcc)
0083   {
0084     // loop over side
0085     for (unsigned int i = 0; i < 2; ++i)
0086     {
0087       // loop over bins in entries
0088       for (int ip = 0; ip < dcc->m_hentries[i]->GetNbinsX(); ++ip)
0089       {
0090         for (int ir = 0; ir < dcc->m_hentries[i]->GetNbinsY(); ++ir)
0091         {
0092           // count number of times a given cell was filled
0093           const auto entries = dcc->m_hentries[i]->GetBinContent(ip + 1, ir + 1);
0094           if (entries <= 1)
0095           {
0096             continue;
0097           }
0098 
0099           // normalize histograms
0100           for (const auto& h : {dcc->m_hDRint[i], dcc->m_hDPint[i], dcc->m_hDZint[i]})
0101           {
0102             h->SetBinContent(ip + 1, ir + 1, h->GetBinContent(ip + 1, ir + 1) / entries);
0103             h->SetBinError(ip + 1, ir + 1, h->GetBinError(ip + 1, ir + 1) / entries);
0104           }
0105         }
0106       }
0107     }
0108   }
0109 
0110   /// fill distortion correction histograms' guarding bins, to allow ::Interpolate to work over the full acceptance
0111   [[maybe_unused]] void fill_guarding_bins(TpcDistortionCorrectionContainer* dcc)
0112   {
0113     // loop over side
0114     for (unsigned int i = 0; i < 2; ++i)
0115     {
0116       for (const auto& h : {dcc->m_hDRint[i], dcc->m_hDPint[i], dcc->m_hDZint[i], dcc->m_hentries[i]})
0117       {
0118         // fill guarding phi bins
0119         /*
0120          * we use 2pi periodicity to do that:
0121          * - last valid bin is copied to first guarding bin;
0122          * - first valid bin is copied to last guarding bin
0123          */
0124         const auto phibins = h->GetNbinsX();
0125         const auto rbins = h->GetNbinsY();
0126         for (int ir = 0; ir < rbins; ++ir)
0127         {
0128           // copy last valid bin to first guarding bin
0129           h->SetBinContent(1, ir + 1, h->GetBinContent(phibins - 1, ir + 1));
0130           h->SetBinError(1, ir + 1, h->GetBinError(phibins - 1, ir + 1));
0131 
0132           // copy first valid bin to last guarding bin
0133           h->SetBinContent(phibins, ir + 1, h->GetBinContent(2, ir + 1));
0134           h->SetBinError(phibins, ir + 1, h->GetBinError(2, ir + 1));
0135         }
0136 
0137         // fill guarding r bins
0138         for (int iphi = 0; iphi < phibins; ++iphi)
0139         {
0140           // copy first valid bin to first guarding bin
0141           h->SetBinContent(iphi + 1, 1, h->GetBinContent(iphi + 1, 2));
0142           h->SetBinError(iphi + 1, 1, h->GetBinError(iphi + 1, 2));
0143 
0144           // copy last valid bin to last guarding bin
0145           h->SetBinContent(iphi + 1, rbins, h->GetBinContent(iphi + 1, rbins - 1));
0146           h->SetBinError(iphi + 1, rbins, h->GetBinError(iphi + 1, rbins - 1));
0147         }
0148       }
0149     }
0150   }
0151 
0152 }  // namespace
0153 
0154 //____________________________________________________________________________..
0155 TpcCentralMembraneMatching::TpcCentralMembraneMatching(const std::string& name)
0156   : SubsysReco(name)
0157 {
0158   // calculate stripes center positions
0159   CalculateCenters(nPads_R1, R1_e, nGoodStripes_R1_e, keepUntil_R1_e, nStripesIn_R1_e, nStripesBefore_R1_e, cx1_e, cy1_e);
0160   CalculateCenters(nPads_R1, R1, nGoodStripes_R1, keepUntil_R1, nStripesIn_R1, nStripesBefore_R1, cx1, cy1);
0161   CalculateCenters(nPads_R2, R2, nGoodStripes_R2, keepUntil_R2, nStripesIn_R2, nStripesBefore_R2, cx2, cy2);
0162   CalculateCenters(nPads_R3, R3, nGoodStripes_R3, keepUntil_R3, nStripesIn_R3, nStripesBefore_R3, cx3, cy3);
0163 }
0164 
0165 //___________________________________________________________
0166 void TpcCentralMembraneMatching::set_grid_dimensions(int phibins, int rbins)
0167 {
0168   m_phibins = phibins;
0169   m_rbins = rbins;
0170 }
0171 
0172 // get the average phi rotation using smoothed histograms
0173 double TpcCentralMembraneMatching::getPhiRotation_smoothed(TH1* hitHist, TH1* clustHist, bool side)
0174 {
0175   // smooth the truth and cluster histograms
0176   hitHist->Smooth();
0177   clustHist->Smooth();
0178 
0179   // make a TF1 with a lambda function to make a function out of the truth histogram and shift it by a constant
0180   TF1* f1 = new TF1("f1", [&](double* x, double* p)
0181                     { return p[0] * hitHist->GetBinContent(hitHist->FindBin((x[0] - p[1]) > M_PI ? x[0] - p[1] - 2 * M_PI : x[0] - p[1])); }, -M_PI, M_PI, 2);
0182   f1->SetParNames("A", "shift");
0183   f1->SetParameters(1.0, 0.0);
0184   f1->SetParLimits(1, -M_PI / 18, M_PI / 18);
0185 
0186   if (side && m_recoRotation[1][1] != -999)
0187   {
0188     f1->SetParameter(1, m_recoRotation[1][1]);
0189   }
0190   if (!side && m_recoRotation[0][1] != -999)
0191   {
0192     f1->SetParameter(1, m_recoRotation[0][1]);
0193   }
0194 
0195   f1->SetNpx(500);
0196 
0197   clustHist->Fit("f1", "IQ");
0198 
0199   //  clustHist->Draw();
0200   // f1->Draw("same");
0201 
0202   return f1->GetParameter(1);
0203 }
0204 
0205 void TpcCentralMembraneMatching::getRegionPhiRotation(bool side)
0206 {
0207   TH1* oddHist[3]{};
0208   TH1* evenHist[3]{};
0209 
0210   TH1* oddTruthHist[3]{};
0211   TH1* evenTruthHist[3]{};
0212 
0213   for (int i = 0; i < (int) m_reco_RPeaks[side].size(); i++)
0214   {
0215     int peak = reco_r_phi[side]->GetYaxis()->FindBin(m_reco_RPeaks[side][i]);
0216 
0217     int region = 2;
0218     if (m_reco_RPeaks[side][i] < 41)
0219     {
0220       region = 0;
0221     }
0222     else if (m_reco_RPeaks[side][i] < 58)
0223     {
0224       region = 1;
0225     }
0226 
0227     if (m_reco_RMatches[side][i] % 2)
0228     {
0229       if (!oddHist[region])
0230       {
0231         oddHist[region] = (TH1*) reco_r_phi[side]->ProjectionX(std::format("oddR{}_{}", region, side).c_str(), peak - 2, peak + 2);
0232       }
0233       else
0234       {
0235         oddHist[region]->Add(reco_r_phi[side]->ProjectionX(std::format("oddR{}_{}", region, side).c_str(), peak - 2, peak + 2));
0236       }
0237     }
0238     else
0239     {
0240       if (!evenHist[region])
0241       {
0242         evenHist[region] = (TH1*) reco_r_phi[side]->ProjectionX(std::format("evenR{}_{}", region, side).c_str(), peak - 2, peak + 2);
0243       }
0244       else
0245       {
0246         evenHist[region]->Add(reco_r_phi[side]->ProjectionX(std::format("evenR{}_{}", region, side).c_str(), peak - 2, peak + 2));
0247       }
0248     }
0249   }
0250 
0251   for (unsigned int i = 0; i < m_truth_RPeaks.size(); i++)
0252   {
0253     int peak = truth_r_phi[side]->GetYaxis()->FindBin(m_truth_RPeaks[i]);
0254 
0255     int region = 2;
0256     if (m_truth_RPeaks[i] < 41)
0257     {
0258       region = 0;
0259     }
0260     else if (m_truth_RPeaks[i] < 58)
0261     {
0262       region = 1;
0263     }
0264 
0265     if (i % 2)
0266     {
0267       if (!oddTruthHist[region])
0268       {
0269         oddTruthHist[region] = (TH1*) truth_r_phi[side]->ProjectionX(std::format("oddTR{}_{}", region, side).c_str(), peak - 1, peak + 1);
0270       }
0271       else
0272       {
0273         oddTruthHist[region]->Add(truth_r_phi[side]->ProjectionX(std::format("oddTR{}_{}", region, side).c_str(), peak - 1, peak + 1));
0274       }
0275     }
0276     else
0277     {
0278       if (!evenTruthHist[region])
0279       {
0280         evenTruthHist[region] = (TH1*) truth_r_phi[side]->ProjectionX(std::format("evenTR{}_{}", region, side).c_str(), peak - 1, peak + 1);
0281       }
0282       else
0283       {
0284         evenTruthHist[region]->Add(truth_r_phi[side]->ProjectionX(std::format("evenTR{}_{}", region, side).c_str(), peak - 1, peak + 1));
0285       }
0286     }
0287   }
0288 
0289   int regions[3]{1, 0, 2};
0290 
0291   double rotationEven[3]{-999, -999, -999};
0292   double rotationOdd[3]{-999, -999, -999};
0293 
0294   for (int region : regions)
0295   {
0296     TF1* fEven = new TF1(
0297         "fEven", [&](double* x, double* p)
0298         { return p[0] * evenTruthHist[region]->GetBinContent(evenTruthHist[region]->FindBin((x[0] - p[1]) > M_PI ? x[0] - p[1] - 2 * M_PI : x[0] - p[1])); },
0299         -M_PI, M_PI, 2);
0300     fEven->SetParNames("A", "shift");
0301     fEven->SetParameters(1.0, 0.0);
0302     fEven->SetParLimits(1, -M_PI / 18, M_PI / 18);
0303     fEven->SetNpx(1000);
0304 
0305     TF1* fOdd = new TF1(
0306         "fOdd", [&](double* x, double* p)
0307         { return p[0] * oddTruthHist[region]->GetBinContent(oddTruthHist[region]->FindBin((x[0] - p[1]) > M_PI ? x[0] - p[1] - 2 * M_PI : x[0] - p[1])); },
0308         -M_PI, M_PI, 2);
0309     fOdd->SetParNames("A", "shift");
0310     fOdd->SetParameters(1.0, 0.0);
0311     fOdd->SetParLimits(1, -M_PI / 18, M_PI / 18);
0312     fOdd->SetNpx(1000);
0313 
0314     if (region != 1)
0315     {
0316       if (rotationEven[1] != -999)
0317       {
0318         fEven->SetParameter(1, rotationEven[1]);
0319       }
0320 
0321       if (rotationOdd[1] != -999)
0322       {
0323         fOdd->SetParameter(1, rotationOdd[1]);
0324       }
0325     }
0326 
0327     evenHist[region]->Fit("fEven", "IQ");
0328     oddHist[region]->Fit("fOdd", "IQ");
0329 
0330     rotationEven[region] = fEven->GetParameter(1);
0331     rotationOdd[region] = fOdd->GetParameter(1);
0332 
0333     m_recoRotation[side][region] = (rotationOdd[region] + rotationEven[region]) / 2.0;
0334   }
0335 
0336   /*
0337     int regions[3] {1,0,2};
0338 
0339     for(int region : regions)
0340     {
0341 
0342       double evenMaxTruth = evenTruthHist[region]->GetMaximum();
0343       double oddMaxTruth = oddTruthHist[region]->GetMaximum();
0344 
0345       std::vector<double> evenTruthPeaks;
0346       std::vector<double> oddTruthPeaks;
0347 
0348       for(int i=2; i<oddTruthHist[region]->GetNbinsX(); i++)
0349       {
0350 
0351         if(evenTruthHist[region]->GetBinContent(i) > 0.15*evenMaxTruth)
0352         {
0353           evenTruthPeaks.push_back(evenTruthHist[region]->GetBinCenter(i));
0354         }
0355 
0356         if(oddTruthHist[region]->GetBinContent(i) > 0.15*oddMaxTruth)
0357         {
0358           oddTruthPeaks.push_back(oddTruthHist[region]->GetBinCenter(i));
0359         }
0360 
0361       }
0362 
0363       double evenMax = evenHist[region]->GetMaximum();
0364       double oddMax = oddHist[region]->GetMaximum();
0365 
0366       std::vector<double> evenRecoPeaks;
0367       std::vector<double> oddRecoPeaks;
0368 
0369 
0370     }
0371 
0372     double threshold = 0.02;
0373     std::vector<std::vector<int>> groupPhi;
0374 
0375     std::vector<double> finalEvenTruthPeaks;
0376 
0377     for (int i = 0; i < (int) evenTruthPeaks.size(); i++)
0378     {
0379       std::vector<int> tmpPhi;
0380       tmpPhi.push_back(i);
0381 
0382       bool closePeak = false;
0383       int currentPeak = -1;
0384 
0385       for (int j = 0; j < (int) finalEvenTruthPeaks.size(); j++)
0386       {
0387         for (int k = 0; k < (int) groupPhi[j].size(); k++)
0388         {
0389           if (fabs(evenTruthPeaks[i] - evenTruthPeaks[groupPhi[j][k]]) <= threshold || fabs(evenTruthPeaks[i] - finalEvenTruthPeaks[j]) <= threshold)
0390           {
0391             closePeak = true;
0392             currentPeak = j;
0393             break;
0394           }
0395         }
0396         if (closePeak)
0397         {
0398           break;
0399         }
0400       }
0401 
0402       if (!closePeak)
0403       {
0404         finalEvenTruthPeaks.push_back(evenTruthPeaks[i]);
0405         groupPhi.push_back(tmpPhi);
0406         tmpPhi.clear();
0407         continue;
0408       }
0409 
0410       groupPhi[currentPeak].push_back(i);
0411       double num = 0.0;
0412       double den = 0.0;
0413       for (int j : groupPhi[currentPeak])
0414       {
0415         double rHeight = evenTruthHist[region]->GetBinContent(evenTruthHist[region]->FindBin(evenTruthPeaks[j]));
0416         num += evenTruthPeaks[j] * rHeight;
0417         den += rHeight;
0418       }
0419 
0420       finalEvenTruthPeaks[currentPeak] = num / den;
0421     }
0422 
0423     std::vector<double> finalOddTruthPeaks;
0424     groupPhi.clear();
0425 
0426     for (int i = 0; i < (int) oddTruthPeaks.size(); i++)
0427     {
0428       std::vector<int> tmpPhi;
0429       tmpPhi.push_back(i);
0430 
0431       bool closePeak = false;
0432       int currentPeak = -1;
0433 
0434       for (int j = 0; j < (int) finalOddTruthPeaks.size(); j++)
0435       {
0436         for (int k = 0; k < (int) groupPhi[j].size(); k++)
0437         {
0438           if (fabs(oddTruthPeaks[i] - oddTruthPeaks[groupPhi[j][k]]) <= threshold || fabs(oddTruthPeaks[i] - finalOddTruthPeaks[j]) <= threshold)
0439           {
0440             closePeak = true;
0441             currentPeak = j;
0442             break;
0443           }
0444         }
0445         if (closePeak)
0446         {
0447           break;
0448         }
0449       }
0450 
0451       if (!closePeak)
0452       {
0453         finalOddTruthPeaks.push_back(oddTruthPeaks[i]);
0454         groupPhi.push_back(tmpPhi);
0455         tmpPhi.clear();
0456         continue;
0457       }
0458 
0459       groupPhi[currentPeak].push_back(i);
0460       double num = 0.0;
0461       double den = 0.0;
0462       for (int j : groupPhi[currentPeak])
0463       {
0464         double rHeight = oddTruthHist[region]->GetBinContent(oddTruthHist[region]->FindBin(oddTruthPeaks[j]));
0465         num += oddTruthPeaks[j] * rHeight;
0466         den += rHeight;
0467       }
0468 
0469       finalOddTruthPeaks[currentPeak] = num / den;
0470     }
0471 
0472     //reco
0473     std::vector<double> finalEvenRecoPeaks;
0474     groupPhi.clear();
0475 
0476     for (int i = 0; i < (int) evenRecoPeaks.size(); i++)
0477     {
0478       std::vector<int> tmpPhi;
0479       tmpPhi.push_back(i);
0480 
0481       bool closePeak = false;
0482       int currentPeak = -1;
0483 
0484       for (int j = 0; j < (int) finalEvenRecoPeaks.size(); j++)
0485       {
0486         for (int k = 0; k < (int) groupPhi[j].size(); k++)
0487         {
0488           if (fabs(evenRecoPeaks[i] - evenRecoPeaks[groupPhi[j][k]]) <= threshold || fabs(evenRecoPeaks[i] - finalEvenRecoPeaks[j]) <= threshold)
0489           {
0490             closePeak = true;
0491             currentPeak = j;
0492             break;
0493           }
0494         }
0495         if (closePeak)
0496         {
0497           break;
0498         }
0499       }
0500 
0501       if (!closePeak)
0502       {
0503         finalEvenRecoPeaks.push_back(evenRecoPeaks[i]);
0504         groupPhi.push_back(tmpPhi);
0505         tmpPhi.clear();
0506         continue;
0507       }
0508 
0509       groupPhi[currentPeak].push_back(i);
0510       double num = 0.0;
0511       double den = 0.0;
0512       for (int j : groupPhi[currentPeak])
0513       {
0514         double rHeight = evenHist[region]->GetBinContent(evenHist[region]->FindBin(evenRecoPeaks[j]));
0515         num += evenRecoPeaks[j] * rHeight;
0516         den += rHeight;
0517       }
0518 
0519       finalEvenRecoPeaks[currentPeak] = num / den;
0520     }
0521 
0522     std::vector<double> finalOddRecoPeaks;
0523     groupPhi.clear();
0524 
0525     for (int i = 0; i < (int) oddRecoPeaks.size(); i++)
0526     {
0527       std::vector<int> tmpPhi;
0528       tmpPhi.push_back(i);
0529 
0530       bool closePeak = false;
0531       int currentPeak = -1;
0532 
0533       for (int j = 0; j < (int) finalOddRecoPeaks.size(); j++)
0534       {
0535         for (int k = 0; k < (int) groupPhi[j].size(); k++)
0536         {
0537           if (fabs(oddRecoPeaks[i] - oddRecoPeaks[groupPhi[j][k]]) <= threshold || fabs(oddRecoPeaks[i] - finalOddRecoPeaks[j]) <= threshold)
0538           {
0539             closePeak = true;
0540             currentPeak = j;
0541             break;
0542           }
0543         }
0544         if (closePeak)
0545         {
0546           break;
0547         }
0548       }
0549 
0550       if (!closePeak)
0551       {
0552         finalOddRecoPeaks.push_back(oddRecoPeaks[i]);
0553         groupPhi.push_back(tmpPhi);
0554         tmpPhi.clear();
0555         continue;
0556       }
0557 
0558       groupPhi[currentPeak].push_back(i);
0559       double num = 0.0;
0560       double den = 0.0;
0561       for (int j : groupPhi[currentPeak])
0562       {
0563         double rHeight = oddHist[region]->GetBinContent(oddHist[region]->FindBin(oddRecoPeaks[j]));
0564         num += oddRecoPeaks[j] * rHeight;
0565         den += rHeight;
0566       }
0567 
0568       finalOddRecoPeaks[currentPeak] = num / den;
0569     }
0570     */
0571 }
0572 
0573 std::vector<int> TpcCentralMembraneMatching::doGlobalRMatching(TH2* r_phi, bool side)
0574 {
0575   TH1D* proj = r_phi->ProjectionY("R_proj");
0576 
0577   if (side)
0578   {
0579     m_m[1] = 0.0;
0580     m_b[1] = 0.0;
0581   }
0582   else
0583   {
0584     m_m[0] = 0.0;
0585     m_b[0] = 0.0;
0586   }
0587 
0588   std::vector<double> rPeaks;
0589   std::vector<double> rHeights;
0590   std::vector<double> finalRPeaks;
0591   std::vector<double> finalRHeights;
0592   std::vector<std::vector<int>> groupR;
0593 
0594   proj->GetXaxis()->SetRangeUser(0, 41);
0595   double maxR1 = proj->GetMaximum();
0596 
0597   proj->GetXaxis()->SetRangeUser(41, 58);
0598   double maxR2 = proj->GetMaximum();
0599 
0600   proj->GetXaxis()->SetRangeUser(58, 100);
0601   double maxR3 = proj->GetMaximum();
0602 
0603   proj->GetXaxis()->SetRange(0, 0);
0604 
0605   std::vector<int> hitMatches;
0606 
0607   for (int i = 2; i < proj->GetNbinsX(); i++)
0608   {
0609     // peak is when content is higher than 0.15* maximum value and content is greater than or equal to both adjacent bins
0610     if ((proj->GetBinCenter(i) < 41.0 && proj->GetBinContent(i) > 0.15 * maxR1) || (proj->GetBinCenter(i) >= 41.0 && proj->GetBinCenter(i) < 58.0 && proj->GetBinContent(i) > 0.15 * maxR2) || (proj->GetBinCenter(i) >= 58.0 && proj->GetBinContent(i) > 0.15 * maxR3))
0611     {
0612       rPeaks.push_back(proj->GetBinCenter(i));
0613       rHeights.push_back(proj->GetBinContent(i));
0614     }
0615   }
0616 
0617   if (rPeaks.size() < 5)
0618   {
0619     if (side)
0620     {
0621       m_reco_RPeaks[1].clear();
0622     }
0623     else
0624     {
0625       m_reco_RPeaks[0].clear();
0626     }
0627 
0628     if (Verbosity())
0629     {
0630       std::cout << (side ? "side 1" : "side 0") << " has fewer than 5 radial peaks (only has " << rPeaks.size() << "). Returning empty vectors" << std::endl;
0631     }
0632     return hitMatches;
0633   }
0634 
0635   double threshold = 0.75;
0636 
0637   for (int i = 0; i < (int) rPeaks.size(); i++)
0638   {
0639     std::vector<int> tmpR;
0640     tmpR.push_back(i);
0641 
0642     bool closePeak = false;
0643     int currentPeak = -1;
0644 
0645     if (rPeaks[i] > 41.0)
0646     {
0647       threshold = 1.0;
0648     }
0649 
0650     for (int j = 0; j < (int) finalRPeaks.size(); j++)
0651     {
0652       for (int k = 0; k < (int) groupR[j].size(); k++)
0653       {
0654         if (fabs(rPeaks[i] - rPeaks[groupR[j][k]]) <= threshold || fabs(rPeaks[i] - finalRPeaks[j]) <= threshold)
0655         {
0656           closePeak = true;
0657           currentPeak = j;
0658           break;
0659         }
0660       }
0661       if (closePeak)
0662       {
0663         break;
0664       }
0665     }
0666 
0667     if (!closePeak)
0668     {
0669       finalRPeaks.push_back(rPeaks[i]);
0670       finalRHeights.push_back(rHeights[i]);
0671       groupR.push_back(tmpR);
0672       tmpR.clear();
0673       continue;
0674     }
0675 
0676     groupR[currentPeak].push_back(i);
0677     double num = 0.0;
0678     double den = 0.0;
0679     for (int j : groupR[currentPeak])
0680     {
0681       double rHeight = proj->GetBinContent(proj->FindBin(rPeaks[j]));
0682       num += rPeaks[j] * rHeight;
0683       den += rHeight;
0684     }
0685 
0686     finalRPeaks[currentPeak] = num / den;
0687     finalRHeights[currentPeak] = den;
0688   }
0689 
0690   if (side)
0691   {
0692     m_reco_RPeaks[1].clear();
0693 
0694     for (double& finalRPeak : finalRPeaks)
0695     {
0696       m_reco_RPeaks[1].push_back(finalRPeak);
0697     }
0698   }
0699   else
0700   {
0701     m_reco_RPeaks[0].clear();
0702 
0703     for (double& finalRPeak : finalRPeaks)
0704     {
0705       m_reco_RPeaks[0].push_back(finalRPeak);
0706     }
0707   }
0708 
0709   if (Verbosity())
0710   {
0711     std::cout << "finalRPeaks: {";
0712     for (int i = 0; i < (int) finalRPeaks.size() - 1; i++)
0713     {
0714       std::cout << finalRPeaks[i] << ", ";
0715     }
0716     std::cout << finalRPeaks[finalRPeaks.size() - 1] << "}" << std::endl;
0717 
0718     if (side)
0719     {
0720       std::cout << "m_reco_RPeaks[1]: {";
0721       for (int i = 0; i < (int) m_reco_RPeaks[1].size() - 1; i++)
0722       {
0723         std::cout << m_reco_RPeaks[1][i] << ", ";
0724       }
0725       std::cout << m_reco_RPeaks[1][m_reco_RPeaks[1].size() - 1] << "}" << std::endl;
0726     }
0727     else
0728     {
0729       std::cout << "m_reco_RPeaks[0]: {";
0730       for (int i = 0; i < (int) m_reco_RPeaks[0].size() - 1; i++)
0731       {
0732         std::cout << m_reco_RPeaks[0][i] << ", ";
0733       }
0734       std::cout << m_reco_RPeaks[0][m_reco_RPeaks[0].size() - 1] << "}" << std::endl;
0735     }
0736 
0737     std::cout << "rHeights: {";
0738     for (int i = 0; i < (int) finalRHeights.size() - 1; i++)
0739     {
0740       std::cout << finalRHeights[i] << ", ";
0741     }
0742     std::cout << finalRHeights[finalRHeights.size() - 1] << "}" << std::endl;
0743   }
0744 
0745   /*
0746   int middle_peak = -1;
0747   double closestPeak = 100000000.0;
0748   for (int i = 0; i < (int) finalRPeaks.size(); i++)
0749   {
0750     if (fabs(m_truth_RPeaks[21] - finalRPeaks[i]) < closestPeak)
0751     {
0752       closestPeak = fabs(m_truth_RPeaks[21] - finalRPeaks[i]);
0753       middle_peak = i;
0754     }
0755   }
0756 
0757   double middle_NN = 100000000.0;
0758   int middle_match = -1;
0759   for (int i = 0; i < (int) m_truth_RPeaks.size(); i++)
0760   {
0761     if (fabs(finalRPeaks[middle_peak] - m_truth_RPeaks[i]) < middle_NN)
0762     {
0763       middle_NN = fabs(finalRPeaks[middle_peak] - m_truth_RPeaks[i]);
0764       middle_match = i;
0765     }
0766   }
0767 
0768   */
0769 
0770   double first_NN = 100000000.0;
0771   int first_match = -1;
0772   for (int i = 0; i < (int) m_truth_RPeaks.size(); i++)
0773   {
0774     if (fabs(finalRPeaks[0] - m_truth_RPeaks[i]) < first_NN)
0775     {
0776       first_NN = fabs(finalRPeaks[0] - m_truth_RPeaks[i]);
0777       first_match = i;
0778     }
0779   }
0780 
0781   double last_NN = 100000000.0;
0782   int last_match = -1;
0783   for (int i = 0; i < (int) m_truth_RPeaks.size(); i++)
0784   {
0785     if (fabs(finalRPeaks[finalRPeaks.size() - 1] - m_truth_RPeaks[i]) < last_NN)
0786     {
0787       last_NN = fabs(finalRPeaks[finalRPeaks.size() - 1] - m_truth_RPeaks[i]);
0788       last_match = i;
0789     }
0790   }
0791 
0792   double bestSum = 100000000.0;
0793   int match_i = 0;
0794   int match_j = 0;
0795 
0796   int minI = -3;
0797   if (first_match + minI < 0)
0798   {
0799     minI = -first_match;
0800   }
0801 
0802   int maxJ = 3;
0803   if (last_match + maxJ >= (int) m_truth_RPeaks.size())
0804   {
0805     maxJ = ((int) m_truth_RPeaks.size()) - 1 - last_match;
0806   }
0807 
0808   std::vector<std::vector<double>> matches(3 - minI + 1, std::vector<double>(maxJ + 3 + 1, -999.0));
0809   std::vector<std::vector<std::vector<int>>> NNMatches(3 - minI + 1, std::vector<std::vector<int>>(maxJ + 3 + 1, std::vector<int>(finalRPeaks.size(), -1)));
0810 
0811   for (int i = minI; i <= 3; i++)
0812   {
0813     for (int j = -3; j <= maxJ; j++)
0814     {
0815       bool goodPair = true;
0816 
0817       if (m_fixShifts)
0818       {
0819         if (m_fieldOn)
0820         {
0821           if (i != -1 || j != -1)
0822           {
0823             goodPair = false;
0824           }
0825         }
0826 
0827         if (!m_fieldOn)
0828         {
0829           if (i != 0 || j != 0)
0830           {
0831             goodPair = false;
0832           }
0833         }
0834       }
0835 
0836       if (!goodPair)
0837       {
0838         continue;
0839       }
0840 
0841       double sum = 0.0;
0842       double m = (m_truth_RPeaks[last_match + j] - m_truth_RPeaks[first_match + i]) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0843       double b = ((m_truth_RPeaks[first_match + i] * finalRPeaks[finalRPeaks.size() - 1]) - (m_truth_RPeaks[last_match + j] * finalRPeaks[0])) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0844       // std::vector<int> tmpMatches;
0845       int recoIndex = 0;
0846       for (double finalRPeak : finalRPeaks)
0847       {
0848         int minMatch = 0;
0849         double minResidual = 1000000000.0;
0850         for (int k = 0; k < (int) m_truth_RPeaks.size(); k++)
0851         {
0852           double residual = fabs((m * finalRPeak + b) - m_truth_RPeaks[k]);
0853           if (residual < minResidual)
0854           {
0855             minResidual = residual;
0856             minMatch = k;
0857             if (minResidual < 0.5)
0858             {
0859               break;
0860             }
0861           }
0862         }
0863         sum += fabs((m * finalRPeak + b) - m_truth_RPeaks[minMatch]);
0864         // tmpMatches.push_back(minMatch);
0865         NNMatches[i - minI][j + 3][recoIndex] = minMatch;
0866         recoIndex++;
0867       }
0868       // NNMatches.push_back(tmpMatches);
0869       // matches.push_back(sum);
0870       matches[i - minI][j + 3] = sum;
0871       if (m_savehistograms)
0872       {
0873         if (side)
0874         {
0875           m_matchResiduals[1]->Fill(i, j, sum);
0876         }
0877         else
0878         {
0879           m_matchResiduals[0]->Fill(i, j, sum);
0880         }
0881       }
0882       if (sum < bestSum)
0883       {
0884         bestSum = sum;
0885         match_i = i - minI;
0886         match_j = j + 3;
0887       }
0888     }
0889   }
0890 
0891   if (Verbosity())
0892   {
0893     std::cout << "best total residual = " << bestSum << "   at i " << match_i + minI << "   j " << match_j - 3 << std::endl;
0894     for (int i = 0; i < (int) matches.size(); i++)
0895     {
0896       for (int j = 0; j < (int) matches[i].size(); j++)
0897       {
0898         if (matches[i][j] == -999.0)
0899         {
0900           continue;
0901         }
0902         std::cout << "total Residual match i=" << i + minI << "   j=" << j - 3 << "   : " << matches[i][j] << std::endl;
0903       }
0904     }
0905   }
0906 
0907   for (int& i : NNMatches[match_i][match_j])
0908   {
0909     hitMatches.push_back(i);
0910   }
0911 
0912   if (Verbosity())
0913   {
0914     for (int i = 0; i < (int) NNMatches.size(); i++)
0915     {
0916       for (int j = 0; j < (int) NNMatches[i].size(); j++)
0917       {
0918         if (matches[i][j] == -999.0)
0919         {
0920           continue;
0921         }
0922         double m = (m_truth_RPeaks[last_match + j - 3] - m_truth_RPeaks[first_match + i + minI]) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0923         double b = ((m_truth_RPeaks[first_match + i + minI] * finalRPeaks[finalRPeaks.size() - 1]) - (m_truth_RPeaks[last_match + j - 3] * finalRPeaks[0])) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0924         for (int k = 0; k < (int) NNMatches[i][j].size(); k++)
0925         {
0926           std::cout << "i " << i + minI << "   j " << j - 3 << "   Reco index " << k << "   recoR=" << finalRPeaks[k] << "   shifted R=" << (m * finalRPeaks[k] + b) << "   matchIndex=" << NNMatches[i][j][k] << "   truth R=" << m_truth_RPeaks[NNMatches[i][j][k]] << "   residual=" << (m * finalRPeaks[k] + b) - m_truth_RPeaks[NNMatches[i][j][k]] << std::endl;
0927         }
0928       }
0929     }
0930   }
0931 
0932   double final_m = (m_truth_RPeaks[last_match + match_j - 3] - m_truth_RPeaks[first_match + match_i + minI]) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0933   double final_b = ((m_truth_RPeaks[first_match + match_i + minI] * finalRPeaks[finalRPeaks.size() - 1]) - (m_truth_RPeaks[last_match + match_j - 3] * finalRPeaks[0])) / (finalRPeaks[finalRPeaks.size() - 1] - finalRPeaks[0]);
0934 
0935   if (side)
0936   {
0937     m_m[1] = final_m;
0938     m_b[1] = final_b;
0939     m_matchLow[1] = match_i + minI;
0940     m_matchHigh[1] = match_j - 3;
0941   }
0942   else
0943   {
0944     m_m[0] = final_m;
0945     m_b[0] = final_b;
0946     m_matchLow[0] = match_i + minI;
0947     m_matchHigh[0] = match_j - 3;
0948   }
0949 
0950   return hitMatches;
0951 }
0952 
0953 int TpcCentralMembraneMatching::getClusterRMatch(double clusterR, int side)
0954 {
0955   double closestDist = 100.;
0956   int closestPeak = -1;
0957 
0958   // find cluster peak closest to position of passed cluster
0959   for (int j = 0; j < (int) m_reco_RPeaks[side].size(); j++)
0960   {
0961     if (std::abs(clusterR - m_reco_RPeaks[side][j]) < closestDist)
0962     {
0963       closestDist = std::abs(clusterR - m_reco_RPeaks[side][j]);
0964       closestPeak = j;
0965     }
0966   }
0967 
0968   // return hit match to cluster peak or -1 if closest peak failed (shouldn't be possible)
0969   if (closestPeak != -1)
0970   {
0971     return m_reco_RMatches[side][closestPeak];
0972   }
0973 
0974   return -1;
0975 }
0976 
0977 //____________________________________________________________________________..
0978 int TpcCentralMembraneMatching::InitRun(PHCompositeNode* topNode)
0979 {
0980   if (!m_fieldOn)
0981   {
0982     m_useHeader = false;
0983   }
0984 
0985   if (m_savehistograms)
0986   {
0987     static constexpr float max_dr = 5.0;
0988     static constexpr float max_dphi = 0.05;
0989 
0990     fout = new TFile(m_histogramfilename.c_str(), "RECREATE");
0991     // fout.reset(new TFile(m_histogramfilename.c_str(), "RECREATE"));
0992     hxy_reco = new TH2F("hxy_reco", "reco cluster x:y", 800, -100, 100, 800, -80, 80);
0993     hxy_truth = new TH2F("hxy_truth", "truth cluster x:y", 800, -100, 100, 800, -80, 80);
0994 
0995     hdrdphi = new TH2F("hdrdphi", "dr vs dphi", 800, -max_dr, max_dr, 800, -max_dphi, max_dphi);
0996     hdrdphi->GetXaxis()->SetTitle("dr");
0997     hdrdphi->GetYaxis()->SetTitle("dphi");
0998 
0999     hrdr = new TH2F("hrdr", "dr vs r", 800, 0.0, 80.0, 800, -max_dr, max_dr);
1000     hrdr->GetXaxis()->SetTitle("r");
1001     hrdr->GetYaxis()->SetTitle("dr");
1002 
1003     hrdphi = new TH2F("hrdphi", "dphi vs r", 800, 0.0, 80.0, 800, -max_dphi, max_dphi);
1004     hrdphi->GetXaxis()->SetTitle("r");
1005     hrdphi->GetYaxis()->SetTitle("dphi");
1006 
1007     hdphi = new TH1F("hdphi", "dph", 800, -max_dphi, max_dphi);
1008     hdphi->GetXaxis()->SetTitle("dphi");
1009 
1010     hdr1_single = new TH1F("hdr1_single", "innner dr single", 200, -max_dr, max_dr);
1011     hdr2_single = new TH1F("hdr2_single", "mid dr single", 200, -max_dr, max_dr);
1012     hdr3_single = new TH1F("hdr3_single", "outer dr single", 200, -max_dr, max_dr);
1013     hdr1_double = new TH1F("hdr1_double", "innner dr double", 200, -max_dr, max_dr);
1014     hdr2_double = new TH1F("hdr2_double", "mid dr double", 200, -max_dr, max_dr);
1015     hdr3_double = new TH1F("hdr3_double", "outer dr double", 200, -max_dr, max_dr);
1016     hdrphi = new TH1F("hdrphi", "r * dphi", 200, -0.05, 0.05);
1017     hnclus = new TH1F("hnclus", " nclusters ", 3, 0., 3.);
1018 
1019     m_debugfile = new TFile(m_debugfilename.c_str(), "RECREATE");
1020     // m_debugfile.reset(new TFile(m_debugfilename.c_str(), "RECREATE"));
1021     match_tree = new TTree("match_tree", "Match TTree");
1022     event_tree = new TTree("event_tree", "Event Tree");
1023 
1024     match_tree->Branch("event", &m_event_index);
1025     match_tree->Branch("matched", &m_matched);
1026     match_tree->Branch("truthIndex", &m_truthIndex);
1027     match_tree->Branch("truthR", &m_truthR);
1028     match_tree->Branch("truthPhi", &m_truthPhi);
1029     match_tree->Branch("recoR", &m_recoR);
1030     match_tree->Branch("recoPhi", &m_recoPhi);
1031     match_tree->Branch("recoZ", &m_recoZ);
1032     match_tree->Branch("rawR", &m_rawR);
1033     match_tree->Branch("rawPhi", &m_rawPhi);
1034     match_tree->Branch("staticR", &m_staticR);
1035     match_tree->Branch("staticPhi", &m_staticPhi);
1036     match_tree->Branch("fitMode", &m_fitMode);
1037     match_tree->Branch("isSaturated", &m_isSaturated);
1038     match_tree->Branch("side", &m_side);
1039     match_tree->Branch("adc", &m_adc);
1040     match_tree->Branch("nhits", &m_nhits);
1041     match_tree->Branch("nLayers", &m_nLayers);
1042     match_tree->Branch("nIPhi", &m_nIPhi);
1043     match_tree->Branch("nIT", &m_nIT);
1044     match_tree->Branch("layerSD", &m_layersSD);
1045     match_tree->Branch("IPhiSD", &m_IPhiSD);
1046     match_tree->Branch("ITSD", &m_ITSD);
1047     match_tree->Branch("layerWeightedSD", &m_layersWeightedSD);
1048     match_tree->Branch("IPhiWeightedSD", &m_IPhiWeightedSD);
1049     match_tree->Branch("ITWeightedSD", &m_ITWeightedSD);
1050     match_tree->Branch("lowShift", &m_lowShift);
1051     match_tree->Branch("highShift", &m_highShift);
1052     match_tree->Branch("phiRotation", &m_phiRotation);
1053     match_tree->Branch("distanceToTruth", &m_distanceToTruth);
1054     match_tree->Branch("NNDistance", &m_NNDistance);
1055     match_tree->Branch("NNR", &m_NNR);
1056     match_tree->Branch("NNPhi", &m_NNPhi);
1057     match_tree->Branch("NNIndex", &m_NNIndex);
1058 
1059     event_tree->Branch("event", &m_event_index);
1060     event_tree->Branch("matched", &e_matched);
1061     event_tree->Branch("truthIndex", &e_truthIndex);
1062     event_tree->Branch("truthR", &e_truthR);
1063     event_tree->Branch("truthPhi", &e_truthPhi);
1064     event_tree->Branch("recoR", &e_recoR);
1065     event_tree->Branch("recoPhi", &e_recoPhi);
1066     event_tree->Branch("side", &e_side);
1067     event_tree->Branch("isSaturated", &e_isSaturated);
1068     event_tree->Branch("fitMode", &e_fitMode);
1069     event_tree->Branch("NNindex", &e_NNIndex);
1070     event_tree->Branch("NNR", &e_NNR);
1071     event_tree->Branch("NNPhi", &e_NNPhi);
1072     event_tree->Branch("NNDistance", &e_NNDistance);
1073     event_tree->Branch("adc", &e_adc);
1074 
1075     // match_ntup = new TNtuple("match_ntup", "Match NTuple", "event:truthR:truthPhi:truthIndex:recoR:recoPhi:recoZ:side:adc:nhits:nLayers:nIPhi:nIT");
1076     m_matchResiduals[0] = new TH2F("matchResiduals_0", "Matching Residuals TPC South;Shift of smallest R from NN match;Shift of largest R from NN match", 7, -3.5, 3.5, 7, -3.5, 3.5);
1077     m_matchResiduals[1] = new TH2F("matchResiduals_1", "Matching Residuals TPC North;Shift of smallest R from NN match;Shift of largest R from NN match", 7, -3.5, 3.5, 7, -3.5, 3.5);
1078   }
1079 
1080   truth_r_phi[0] = new TH2F("truth_r_phi_0", "truth r vs #phi side 0;#phi (rad); r (cm)", 360, -M_PI, M_PI, 500, 0, 100);
1081   truth_r_phi[1] = new TH2F("truth_r_phi_1", "truth r vs #phi side 1;#phi (rad); r (cm)", 360, -M_PI, M_PI, 500, 0, 100);
1082 
1083   reco_r_phi[0] = new TH2F("reco_r_phi_0", "reco R vs #phi side 0;#phi (rad); r (cm)", 360, -M_PI, M_PI, 350, 20, 90);
1084   reco_r_phi[1] = new TH2F("reco_r_phi_1", "reco R vs #phi side 1;#phi (rad); r (cm)", 360, -M_PI, M_PI, 350, 20, 90);
1085 
1086   // Get truth cluster positions
1087   //=====================
1088 
1089   const double phi_petal = M_PI / 9.0;  // angle span of one petal
1090 
1091   /*
1092    * utility function to
1093    * - duplicate generated truth position to cover both sides of the central membrane
1094    * - assign proper z,
1095    * - insert in container
1096    */
1097   auto save_truth_position = [&](TVector3 source)
1098   {
1099     source.SetZ(-1);
1100     source.RotateZ(-M_PI / 18);
1101     m_truth_pos.push_back(source);
1102     truth_r_phi[0]->Fill(source.Phi(), source.Perp());
1103 
1104     source.SetZ(+1);
1105     source.RotateZ(M_PI / 18);
1106     source.SetX(-1 * source.X());
1107     m_truth_pos.push_back(source);
1108     truth_r_phi[1]->Fill(source.Phi(), source.Perp());
1109   };
1110 
1111   // inner region extended is the 8 layers inside 30 cm
1112   for (int j = 0; j < nRadii; ++j)
1113   {
1114     for (int i = 0; i < nGoodStripes_R1_e[j]; ++i)
1115     {
1116       for (int k = 0; k < 18; ++k)
1117       {
1118         TVector3 dummyPos(cx1_e[i][j], cy1_e[i][j], 0.0);
1119         dummyPos.RotateZ(k * phi_petal);
1120         save_truth_position(dummyPos);
1121         // int truth_index_1 = k*10000 + j*100 + i;
1122         // int truth_index_1 = k*10000 + j*100 + (nGoodStripes_R1_e[j]-i-1);
1123         int truth_index_1;
1124         int truth_index_0;
1125         if (k <= 8)
1126         {
1127           truth_index_0 = (26 - k) * 10000 + j * 100 + (nGoodStripes_R1_e[j] - i - 1);
1128           truth_index_1 = (8 - k) * 10000 + j * 100 + (nGoodStripes_R1_e[j] - i - 1);
1129         }
1130         else
1131         {
1132           truth_index_0 = (44 - k) * 10000 + j * 100 + (nGoodStripes_R1_e[j] - i - 1);
1133           truth_index_1 = (26 - k) * 10000 + j * 100 + (nGoodStripes_R1_e[j] - i - 1);
1134         }
1135         m_truth_index.push_back(truth_index_0);
1136         m_truth_index.push_back(truth_index_1);
1137 
1138         if (Verbosity() > 2)
1139         {
1140           std::cout << " i " << i << " j " << j << " k " << k << " x1 " << dummyPos.X() << " y1 " << dummyPos.Y()
1141                     << " theta " << std::atan2(dummyPos.Y(), dummyPos.X())
1142                     << " radius " << get_r(dummyPos.X(), dummyPos.y()) << std::endl;
1143         }
1144         if (m_savehistograms)
1145         {
1146           hxy_truth->Fill(dummyPos.X(), dummyPos.Y());
1147         }
1148       }
1149     }
1150   }
1151 
1152   // inner region is the 8 layers outside 30 cm
1153   for (int j = 0; j < nRadii; ++j)
1154   {
1155     for (int i = 0; i < nGoodStripes_R1[j]; ++i)
1156     {
1157       for (int k = 0; k < 18; ++k)
1158       {
1159         TVector3 dummyPos(cx1[i][j], cy1[i][j], 0.0);
1160         dummyPos.RotateZ(k * phi_petal);
1161         save_truth_position(dummyPos);
1162         // int truth_index_1 = k*10000 + (j+8)*100 + i;
1163         // int truth_index_1 = k*10000 + (j+8)*100 + (nGoodStripes_R1[j]-i-1);
1164         int truth_index_1;
1165         int truth_index_0;
1166         if (k <= 8)
1167         {
1168           truth_index_0 = (26 - k) * 10000 + (j + 8) * 100 + (nGoodStripes_R1[j] - i - 1);
1169           truth_index_1 = (8 - k) * 10000 + (j + 8) * 100 + (nGoodStripes_R1[j] - i - 1);
1170         }
1171         else
1172         {
1173           truth_index_0 = (44 - k) * 10000 + (j + 8) * 100 + (nGoodStripes_R1[j] - i - 1);
1174           truth_index_1 = (26 - k) * 10000 + (j + 8) * 100 + (nGoodStripes_R1[j] - i - 1);
1175         }
1176         m_truth_index.push_back(truth_index_0);
1177         m_truth_index.push_back(truth_index_1);
1178 
1179         if (Verbosity() > 2)
1180         {
1181           std::cout << " i " << i << " j " << j << " k " << k << " x1 " << dummyPos.X() << " y1 " << dummyPos.Y()
1182                     << " theta " << std::atan2(dummyPos.Y(), dummyPos.X())
1183                     << " radius " << get_r(dummyPos.X(), dummyPos.y()) << std::endl;
1184         }
1185         if (m_savehistograms)
1186         {
1187           hxy_truth->Fill(dummyPos.X(), dummyPos.Y());
1188         }
1189       }
1190     }
1191   }
1192 
1193   for (int j = 0; j < nRadii; ++j)
1194   {
1195     for (int i = 0; i < nGoodStripes_R2[j]; ++i)
1196     {
1197       for (int k = 0; k < 18; ++k)
1198       {
1199         TVector3 dummyPos(cx2[i][j], cy2[i][j], 0.0);
1200         dummyPos.RotateZ(k * phi_petal);
1201         save_truth_position(dummyPos);
1202         // int truth_index_1 = k*10000 + (j+16)*100 + i;
1203         // int truth_index_1 = k*10000 + (j+16)*100 + (nGoodStripes_R2[j]-i-1);
1204         int truth_index_1;
1205         int truth_index_0;
1206         if (k <= 8)
1207         {
1208           truth_index_0 = (26 - k) * 10000 + (j + 16) * 100 + (nGoodStripes_R2[j] - i - 1);
1209           truth_index_1 = (8 - k) * 10000 + (j + 16) * 100 + (nGoodStripes_R2[j] - i - 1);
1210         }
1211         else
1212         {
1213           truth_index_0 = (44 - k) * 10000 + (j + 16) * 100 + (nGoodStripes_R2[j] - i - 1);
1214           truth_index_1 = (26 - k) * 10000 + (j + 16) * 100 + (nGoodStripes_R2[j] - i - 1);
1215         }
1216         m_truth_index.push_back(truth_index_0);
1217         m_truth_index.push_back(truth_index_1);
1218 
1219         if (Verbosity() > 2)
1220         {
1221           std::cout << " i " << i << " j " << j << " k " << k << " x1 " << dummyPos.X() << " y1 " << dummyPos.Y()
1222                     << " theta " << std::atan2(dummyPos.Y(), dummyPos.X())
1223                     << " radius " << get_r(dummyPos.X(), dummyPos.y()) << std::endl;
1224         }
1225         if (m_savehistograms)
1226         {
1227           hxy_truth->Fill(dummyPos.X(), dummyPos.Y());
1228         }
1229       }
1230     }
1231   }
1232 
1233   for (int j = 0; j < nRadii; ++j)
1234   {
1235     for (int i = 0; i < nGoodStripes_R3[j]; ++i)
1236     {
1237       for (int k = 0; k < 18; ++k)
1238       {
1239         TVector3 dummyPos(cx3[i][j], cy3[i][j], 0.0);
1240         dummyPos.RotateZ(k * phi_petal);
1241         save_truth_position(dummyPos);
1242         // int truth_index_1 = k*10000 + (j+24)*100 + i;
1243         // int truth_index_1 = k*10000 + (j+24)*100 + (nGoodStripes_R3[j]-i-1);
1244         int truth_index_1;
1245         int truth_index_0;
1246         if (k <= 8)
1247         {
1248           truth_index_0 = (26 - k) * 10000 + (j + 24) * 100 + (nGoodStripes_R3[j] - i - 1);
1249           truth_index_1 = (8 - k) * 10000 + (j + 24) * 100 + (nGoodStripes_R3[j] - i - 1);
1250         }
1251         else
1252         {
1253           truth_index_0 = (44 - k) * 10000 + (j + 24) * 100 + (nGoodStripes_R3[j] - i - 1);
1254           truth_index_1 = (26 - k) * 10000 + (j + 24) * 100 + (nGoodStripes_R3[j] - i - 1);
1255         }
1256         m_truth_index.push_back(truth_index_0);
1257         m_truth_index.push_back(truth_index_1);
1258 
1259         if (Verbosity() > 2)
1260         {
1261           std::cout << " i " << i << " j " << j << " k " << k << " x1 " << dummyPos.X() << " y1 " << dummyPos.Y()
1262                     << " theta " << std::atan2(dummyPos.Y(), dummyPos.X())
1263                     << " radius " << get_r(dummyPos.X(), dummyPos.y()) << std::endl;
1264         }
1265         if (m_savehistograms)
1266         {
1267           hxy_truth->Fill(dummyPos.X(), dummyPos.Y());
1268         }
1269       }
1270     }
1271   }
1272 
1273   /*
1274   int count[2] = {0, 0};
1275 
1276   TFile *lamFile = new TFile(m_lamfilename.c_str(), "READ");
1277   if(lamFile && !lamFile->IsZombie())
1278   {
1279     //std::cout << "got lamination file: " << m_lamfilename << std::endl;
1280     TTree *lamTree = (TTree *) lamFile->Get("laminationTree");
1281 
1282     //std::cout << "got lamination tree" << std::endl;
1283 
1284     double A = 0.0;
1285     double B = 0.0;
1286     double C = 0.0;
1287     double lamPhi = 0.0;
1288     bool side = false;
1289     bool isGoodFit = false;
1290 
1291     lamTree->SetBranchAddress("A", &A);
1292     lamTree->SetBranchAddress("B", &B);
1293     lamTree->SetBranchAddress("C", &C);
1294     lamTree->SetBranchAddress("lamPhi", &lamPhi);
1295     lamTree->SetBranchAddress("side", &side);
1296     lamTree->SetBranchAddress("goodFit", &isGoodFit);
1297 
1298     int nEntries = lamTree->GetEntries();
1299     for (int i = 0; i < nEntries; i++)
1300     {
1301       lamTree->GetEntry(i);
1302       if (!isGoodFit) continue;
1303       meanA[(int) side] += A;
1304       meanB[(int) side] += B;
1305       meanC[(int) side] += C;
1306       count[(int) side]++;
1307       lamPhis[side].push_back(lamPhi);
1308     }
1309 
1310     for(int s=0; s<2; s++)
1311     {
1312       if(count[s]>0)
1313       {
1314         meanA[s] /= count[s];
1315         meanB[s] /= count[s];
1316         meanC[s] /= count[s];
1317       }
1318       else
1319       {
1320         meanA[s] = 0.0;
1321         meanB[s] = 0.0;
1322         meanC[s] = 0.0;
1323       }
1324 
1325       //std::cout << "side " << s << "  meanA " << meanA[s] << "  meanB " << meanB[s] << "  meanC " << meanC[s] << std::endl;
1326     }
1327   }
1328   */
1329 
1330   for (int s = 0; s < 2; s++)
1331   {
1332     gr_dR[s] = new TGraph2D();
1333     gr_dPhi[s] = new TGraph2D();
1334 
1335     gr_points[s] = new TGraph();
1336     gr_points[s]->SetMarkerStyle(20);
1337     gr_points[s]->SetMarkerSize(0.5);
1338     gr_points[s]->SetMarkerColor(kBlack);
1339   }
1340 
1341   int ret = GetNodes(topNode);
1342   return ret;
1343 }
1344 
1345 //____________________________________________________________________________..
1346 int TpcCentralMembraneMatching::process_event(PHCompositeNode* topNode)
1347 {
1348   e_matched.clear();
1349   e_truthIndex.clear();
1350   e_truthR.clear();
1351   e_truthPhi.clear();
1352   e_recoR.clear();
1353   e_recoPhi.clear();
1354   e_side.clear();
1355   e_isSaturated.clear();
1356   e_fitMode.clear();
1357   e_NNR.clear();
1358   e_NNPhi.clear();
1359   e_NNIndex.clear();
1360   e_adc.clear();
1361 
1362   for (int s = 0; s < 2; s++)
1363   {
1364     gr_dR[s]->Clear();
1365     gr_dPhi[s]->Clear();
1366   }
1367 
1368   std::vector<TVector3> reco_pos;
1369   std::vector<TVector3> static_pos;
1370   std::vector<TVector3> raw_pos;
1371   std::vector<bool> reco_side;
1372   std::vector<bool> fitMode;
1373   std::vector<bool> isSaturated;
1374   std::vector<unsigned int> reco_nhits;
1375   std::vector<unsigned int> reco_adc;
1376   std::vector<unsigned int> reco_nLayers;
1377   std::vector<unsigned int> reco_nIPhi;
1378   std::vector<unsigned int> reco_nIT;
1379   std::vector<float> reco_SDLayer;
1380   std::vector<float> reco_SDIPhi;
1381   std::vector<float> reco_SDIT;
1382   std::vector<float> reco_SDWeightedLayer;
1383   std::vector<float> reco_SDWeightedIPhi;
1384   std::vector<float> reco_SDWeightedIT;
1385 
1386   // TF1 *laminationFunc = new TF1("laminationFunc", "[3] + [0]*(1 - exp(-[2]*(x-[1])))", 30,80);
1387 
1388   eventHeader = findNode::getClass<EventHeader>(topNode, "EventHeader");
1389   if (!eventHeader)
1390   {
1391     std::cout << PHWHERE << " EventHeader Node missing, abort" << std::endl;
1392     return Fun4AllReturnCodes::ABORTRUN;
1393   }
1394 
1395   if (m_useHeader && eventHeader->get_EvtSequence() == 0)
1396   {
1397     m_useHeader = false;
1398   }
1399 
1400   if (m_useHeader)
1401   {
1402     m_event_index = eventHeader->get_EvtSequence();
1403   }
1404 
1405   LaserEventInfo* lasereventinfo = findNode::getClass<LaserEventInfo>(topNode, "LaserEventInfo");
1406   if (!lasereventinfo)
1407   {
1408     std::cout << PHWHERE << " LaserEvetnInfo Node missing, abort" << std::endl;
1409     return Fun4AllReturnCodes::ABORTRUN;
1410   }
1411 
1412   if ((eventHeader->get_RunNumber() > 66153 && !lasereventinfo->isGl1LaserEvent()) || (eventHeader->get_RunNumber() <= 66153 && !lasereventinfo->isLaserEvent()) || !m_corrected_CMcluster_map)
1413   // if (!m_corrected_CMcluster_map || m_corrected_CMcluster_map->size() < 1000)
1414   {
1415     if (!m_useHeader)
1416     {
1417       m_event_index++;
1418     }
1419     return Fun4AllReturnCodes::EVENT_OK;
1420   }
1421 
1422   if (Verbosity())
1423   {
1424     std::cout << PHWHERE << "   working on event " << m_event_index << " which has " << m_corrected_CMcluster_map->size() << " clusters" << std::endl;
1425   }
1426 
1427   // reset output distortion correction container histograms
1428   for (const auto& harray : {m_dcc_out->m_hDRint, m_dcc_out->m_hDPint, m_dcc_out->m_hDZint, m_dcc_out->m_hentries})
1429   {
1430     for (const auto& h : harray)
1431     {
1432       h->Reset();
1433     }
1434   }
1435 
1436   reco_r_phi[0]->Reset();
1437   reco_r_phi[1]->Reset();
1438 
1439   int nClus_gtMin = 0;
1440   int clusterIndex = 0;
1441 
1442   // read the reconstructed CM clusters
1443   auto clusrange = m_corrected_CMcluster_map->getClusters();
1444   for (auto cmitr = clusrange.first;
1445        cmitr != clusrange.second;
1446        ++cmitr)
1447   {
1448     const auto& [cmkey, cmclus_orig] = *cmitr;
1449     // CMFlashCluster *cmclus = cmclus_orig;
1450     LaserCluster* cmclus = cmclus_orig;
1451     const unsigned int nhits = cmclus->getNhits();
1452     // const unsigned int nclus = cmclus->getNclusters();
1453     const unsigned int adc = cmclus->getAdc();
1454     bool side = (bool) TpcDefs::getSide(cmkey);
1455 
1456     // std::cout << "cluster " << clusterIndex << " side=" << side << "   sideKey=" << sideKey << "   are they the same? " << (side == sideKey ? "true" : "false") << std::endl;
1457     //  if(m_useOnly_nClus2 && nclus != 2) continue;
1458 
1459     nClus_gtMin++;
1460 
1461     // Do the static + average distortion corrections if the container was found
1462     // since incorrect z values are in cluster do to wrong t0 of laser flash, fixing based on the side for now
1463     // Acts::Vector3 pos(cmclus->getX(), cmclus->getY(), cmclus->getZ());
1464     Acts::Vector3 pos(cmclus->getX(), cmclus->getY(), (side ? 1.0 : -1.0));
1465     TVector3 tmp_raw(pos[0], pos[1], pos[2]);
1466     if (m_dcc_in_module_edge)
1467     {
1468       pos = m_distortionCorrection.get_corrected_position(pos, m_dcc_in_module_edge);
1469     }
1470     if (m_dcc_in_static)
1471     {
1472       pos = m_distortionCorrection.get_corrected_position(pos, m_dcc_in_static);
1473     }
1474     TVector3 tmp_static(pos[0], pos[1], pos[2]);
1475 
1476     if (m_dcc_in_average)
1477     {
1478       pos = m_distortionCorrection.get_corrected_position(pos, m_dcc_in_average);
1479     }
1480     TVector3 tmp_pos(pos[0], pos[1], pos[2]);
1481 
1482     /*
1483     if(meanA[side] != 0.0 && meanB[side] != 0.0 && meanC[side] != 0.0 && lamPhis[side].size()>0)
1484     {
1485       //std::cout << " applying lamination correction for side " << side << " with A " << meanA[side] << " B " << meanB[side] << " C " << meanC[side] << " for phi " << tmp_pos.Phi() << std::endl;
1486       int lamIndex = -1;
1487       double phi = tmp_pos.Phi();
1488       for(int i=0; i<(int)lamPhis[side].size(); i++)
1489       {
1490         if(fabs(phi - lamPhis[side][i]) < 0.2) // 10 degrees
1491         {
1492           lamIndex = i;
1493           break;
1494         }
1495       }
1496 
1497       if(lamIndex != -1)
1498       {
1499         laminationFunc->SetParameter(0, meanA[side]);
1500         laminationFunc->SetParameter(1, meanB[side]);
1501         laminationFunc->SetParameter(2, meanC[side]);
1502         laminationFunc->SetParameter(3, lamPhis[side][lamIndex]);
1503         if(fabs(phi - laminationFunc->Eval(tmp_pos.Perp())) < 0.01) continue;
1504       }
1505     }
1506     */
1507 
1508     // if(nclus == 1 && isRGap) continue;
1509 
1510     bool tmpIsSaturated = false;
1511     for (int h = 0; h < (int) cmclus->getNhits(); h++)
1512     {
1513       if (cmclus->getHitAdc(h) > 900)
1514       {
1515         tmpIsSaturated = true;
1516         break;
1517       }
1518     }
1519 
1520     reco_pos.push_back(tmp_pos);
1521     static_pos.push_back(tmp_static);
1522     raw_pos.push_back(tmp_raw);
1523     reco_side.push_back(side);
1524     fitMode.push_back(cmclus->getFitMode());
1525     isSaturated.push_back(tmpIsSaturated);
1526     reco_nhits.push_back(nhits);
1527     reco_adc.push_back(adc);
1528     reco_nLayers.push_back(cmclus->getNLayers());
1529     reco_nIPhi.push_back(cmclus->getNIPhi());
1530     reco_nIT.push_back(cmclus->getNIT());
1531     reco_SDLayer.push_back(cmclus->getSDLayer());
1532     reco_SDIPhi.push_back(cmclus->getSDIPhi());
1533     reco_SDIT.push_back(cmclus->getSDIT());
1534     reco_SDWeightedLayer.push_back(cmclus->getSDWeightedLayer());
1535     reco_SDWeightedIPhi.push_back(cmclus->getSDWeightedIPhi());
1536     reco_SDWeightedIT.push_back(cmclus->getSDWeightedIT());
1537 
1538     if (side == 0)
1539     {
1540       reco_r_phi[0]->Fill(tmp_pos.Phi(), tmp_pos.Perp());
1541     }
1542     else
1543     {
1544       reco_r_phi[1]->Fill(tmp_pos.Phi(), tmp_pos.Perp());
1545     }
1546 
1547     if (Verbosity() > 2)
1548     {
1549       double raw_rad = std::sqrt(cmclus->getX() * cmclus->getX() + cmclus->getY() * cmclus->getY());
1550       double static_rad = sqrt(tmp_static.X() * tmp_static.X() + tmp_static.Y() * tmp_static.Y());
1551       double corr_rad = sqrt(tmp_pos.X() * tmp_pos.X() + tmp_pos.Y() * tmp_pos.Y());
1552       std::cout << "cluster " << clusterIndex << std::endl;
1553       clusterIndex++;
1554       std::cout << "found raw cluster " << cmkey << " side " << side << " with x " << cmclus->getX() << " y " << cmclus->getY() << " z " << cmclus->getZ() << " radius " << raw_rad << std::endl;
1555       std::cout << "        --- static corrected positions: " << tmp_static.X() << "  " << tmp_static.Y() << "  " << tmp_static.Z() << " radius " << static_rad << std::endl;
1556       std::cout << "                --- corrected positions: " << tmp_pos.X() << "  " << tmp_pos.Y() << "  " << tmp_pos.Z() << " radius " << corr_rad << std::endl;
1557     }
1558 
1559     if (m_savehistograms)
1560     {
1561       hxy_reco->Fill(tmp_pos.X(), tmp_pos.Y());
1562     }
1563   }
1564 
1565   if (nClus_gtMin < 25)
1566   {
1567     if (!m_useHeader)
1568     {
1569       m_event_index++;
1570     }
1571     return Fun4AllReturnCodes::EVENT_OK;
1572   }
1573 
1574   int truth_index = 0;
1575   int nMatched = 0;
1576   std::vector<bool> truth_matched(m_truth_pos.size(), false);
1577   std::vector<bool> reco_matched(reco_pos.size(), false);
1578   std::vector<int> truth_matchedRecoIndex(m_truth_pos.size(), -1);
1579   std::vector<int> reco_matchedTruthIndex(reco_pos.size(), -1);
1580 
1581   std::vector<float> reco_distToNN(reco_pos.size(), 10000.0);
1582   std::vector<float> NNDist(reco_pos.size(), 100000.0);
1583   std::vector<float> NNR(reco_pos.size(), 100000.0);
1584   std::vector<float> NNPhi(reco_pos.size(), 100000.0);
1585   std::vector<int> NNIndex(reco_pos.size(), -1);
1586 
1587   std::vector<std::vector<int>> truth_NNRecoIndex(m_truth_pos.size(), std::vector<int>(0));
1588 
1589   if (m_doFancy)
1590   {
1591     // get global phi rotation for each module
1592     m_recoRotation[0][1] = getPhiRotation_smoothed(truth_r_phi[0]->ProjectionX("hR2", 206, 290), reco_r_phi[0]->ProjectionX("cR2_0", 206, 290), false);
1593     m_recoRotation[0][0] = getPhiRotation_smoothed(truth_r_phi[0]->ProjectionX("hR1", 151, 206), reco_r_phi[0]->ProjectionX("cR1_0", 151, 206), false);
1594     m_recoRotation[0][2] = getPhiRotation_smoothed(truth_r_phi[0]->ProjectionX("hR3", 290, 499), reco_r_phi[0]->ProjectionX("cR3_0", 290, 499), false);
1595 
1596     m_recoRotation[1][1] = getPhiRotation_smoothed(truth_r_phi[1]->ProjectionX("hR2", 206, 290), reco_r_phi[1]->ProjectionX("cR2_1", 206, 290), true);
1597     m_recoRotation[1][0] = getPhiRotation_smoothed(truth_r_phi[1]->ProjectionX("hR1", 151, 206), reco_r_phi[1]->ProjectionX("cR1_1", 151, 206), true);
1598     m_recoRotation[1][2] = getPhiRotation_smoothed(truth_r_phi[1]->ProjectionX("hR3", 290, 499), reco_r_phi[1]->ProjectionX("cR3_1", 290, 499), true);
1599 
1600     m_reco_RMatches[0] = doGlobalRMatching(reco_r_phi[0], false);
1601     m_reco_RMatches[1] = doGlobalRMatching(reco_r_phi[1], true);
1602 
1603     if (Verbosity() > 2)
1604     {
1605       for (int i = 0; i < (int) m_reco_RMatches[0].size(); i++)
1606       {
1607         std::cout << "side 0 cluster index " << i << "   hit match " << m_reco_RMatches[0][i] << "   recoPeak=" << m_reco_RPeaks[0][i] << "   shifted recoPeak=" << (m_m[0] * m_reco_RPeaks[0][i] + m_b[0]) << "   truthPeak=" << m_truth_RPeaks[m_reco_RMatches[0][i]] << "   residual=" << m_truth_RPeaks[m_reco_RMatches[0][i]] - (m_m[0] * m_reco_RPeaks[0][i] + m_b[0]) << std::endl;
1608       }
1609 
1610       for (int i = 0; i < (int) m_reco_RMatches[1].size(); i++)
1611       {
1612         std::cout << "side 1 cluster index " << i << "   hit match " << m_reco_RMatches[1][i] << "   recoPeak=" << m_reco_RPeaks[1][i] << "   shifted recoPeak=" << (m_m[1] * m_reco_RPeaks[1][i] + m_b[1]) << "   truthPeak=" << m_truth_RPeaks[m_reco_RMatches[1][i]] << "   residual=" << m_truth_RPeaks[m_reco_RMatches[1][i]] - (m_m[1] * m_reco_RPeaks[1][i] + m_b[1]) << std::endl;
1613       }
1614     }
1615 
1616     for (const auto& truth : m_truth_pos)
1617     {
1618       double tR = get_r(truth.X(), truth.Y());
1619       double tPhi = truth.Phi();
1620       double tZ = truth.Z();
1621 
1622       int truthRIndex = -1;
1623 
1624       // get which hit radial index this it
1625       for (int k = 0; k < (int) m_truth_RPeaks.size(); k++)
1626       {
1627         if (std::abs(tR - m_truth_RPeaks[k]) < 0.5)
1628         {
1629           truthRIndex = k;
1630           break;
1631         }
1632       }
1633 
1634       if (truthRIndex == -1)
1635       {
1636         truth_index++;
1637         continue;
1638       }
1639 
1640       double prev_dphi = 10000.0;
1641 
1642       int recoMatchIndex = -1;
1643       unsigned int reco_index = 0;
1644       for (const auto& reco : reco_pos)
1645       {
1646         if (reco_matched[reco_index] || reco_nhits[reco_index] < m_nHitsInCuster_minimum)
1647         {
1648           reco_index++;
1649           continue;
1650         }
1651 
1652         double rR = get_r(reco.X(), reco.Y());
1653         double rPhi = reco.Phi();
1654         bool side = reco_side[reco_index];
1655 
1656         int region = -1;
1657 
1658         if (rR < 41)
1659         {
1660           region = 0;
1661         }
1662         else if (rR >= 41 && rR < 58)
1663         {
1664           region = 1;
1665         }
1666         else if (rR >= 58)
1667         {
1668           region = 2;
1669         }
1670 
1671         if (region != -1)
1672         {
1673           if (side)
1674           {
1675             rPhi -= m_recoRotation[1][region];
1676           }
1677           else
1678           {
1679             rPhi -= m_recoRotation[0][region];
1680           }
1681         }
1682 
1683         int clustRMatchIndex = -1;
1684         clustRMatchIndex = getClusterRMatch(rR, (side ? 1 : 0));
1685 
1686         if (clustRMatchIndex == -1 || truthRIndex != clustRMatchIndex)
1687         {
1688           reco_index++;
1689           continue;
1690         }
1691 
1692         if ((!side && tZ > 0) || (side && tZ < 0))
1693         {
1694           reco_index++;
1695           continue;
1696         }
1697 
1698         auto dphi = delta_phi(tPhi - rPhi);
1699         if (fabs(dphi) > m_phi_cut)
1700         {
1701           reco_index++;
1702           continue;
1703         }
1704 
1705         if (fabs(dphi) < fabs(prev_dphi))
1706         {
1707           prev_dphi = dphi;
1708           recoMatchIndex = reco_index;
1709           truth_matched[truth_index] = true;
1710         }
1711 
1712         reco_index++;
1713       }  // end loop over reco
1714 
1715       if (recoMatchIndex != -1)
1716       {
1717         truth_matchedRecoIndex[truth_index] = recoMatchIndex;
1718         reco_matched[recoMatchIndex] = true;
1719         reco_matchedTruthIndex[recoMatchIndex] = truth_index;
1720         nMatched++;
1721 
1722         if (Verbosity() > 2)
1723         {
1724           std::cout << "truth " << truth_index << " matched to reco " << recoMatchIndex << " and there are now " << nMatched << " matches" << std::endl;
1725           std::cout << "tR=" << std::setw(10) << tR << " tPhi=" << std::setw(10) << tPhi << " tZ=" << std::setw(10) << tZ << std::endl;
1726           std::cout << "rR=" << std::setw(10) << get_r(reco_pos[recoMatchIndex].X(), reco_pos[recoMatchIndex].Y()) << " rPhi=" << std::setw(10) << reco_pos[recoMatchIndex].Phi() << " rZ=" << std::setw(10) << reco_pos[recoMatchIndex].Z() << " rSide=" << reco_side[recoMatchIndex] << "   dPhi=" << prev_dphi << std::endl;
1727         }
1728       }
1729 
1730       truth_index++;
1731     }  // end loop over truth
1732 
1733     // loop again to find nearest neighbor for unmatched reco clusters
1734 
1735     int recoIndex = 0;
1736     for (const auto& reco : reco_pos)
1737     {
1738       double rR = get_r(reco.X(), reco.Y());
1739       double rPhi = reco.Phi();
1740       bool side = reco_side[recoIndex];
1741 
1742       int region = -1;
1743 
1744       if (rR < 41)
1745       {
1746         region = 0;
1747       }
1748       else if (rR >= 41 && rR < 58)
1749       {
1750         region = 1;
1751       }
1752       else if (rR >= 58)
1753       {
1754         region = 2;
1755       }
1756 
1757       if (region != -1)
1758       {
1759         if (side)
1760         {
1761           rPhi -= m_recoRotation[1][region];
1762         }
1763         else
1764         {
1765           rPhi -= m_recoRotation[0][region];
1766         }
1767       }
1768 
1769       int clustRMatchIndex = -1;
1770       clustRMatchIndex = getClusterRMatch(rR, (side ? 1 : 0));
1771 
1772       int truthMatchIndex = -1;
1773       truth_index = 0;
1774       for (const auto& truth : m_truth_pos)
1775       {
1776         double tR = get_r(truth.X(), truth.Y());
1777         double tPhi = truth.Phi();
1778         double tZ = truth.Z();
1779 
1780         if ((!side && tZ > 0) || (side && tZ < 0))
1781         {
1782           truth_index++;
1783           continue;
1784         }
1785 
1786         if (sqrt(pow(truth.X() - reco.X(), 2) + pow(truth.Y() - reco.Y(), 2)) < NNDist[recoIndex])
1787         {
1788           NNDist[recoIndex] = sqrt(pow(truth.X() - reco.X(), 2) + pow(truth.Y() - reco.Y(), 2));
1789           NNR[recoIndex] = tR;
1790           NNPhi[recoIndex] = tPhi;
1791           NNIndex[recoIndex] = m_truth_index[truth_index];
1792         }
1793 
1794         if (clustRMatchIndex == -1)
1795         {
1796           continue;
1797         }
1798 
1799         if (reco_matched[recoIndex])
1800         {
1801           if (reco_matchedTruthIndex[recoIndex] == truth_index)
1802           {
1803             reco_distToNN[recoIndex] = sqrt(pow(truth.X() - reco.X(), 2) + pow(truth.Y() - reco.Y(), 2));
1804             break;
1805           }
1806 
1807           truth_index++;
1808           continue;
1809         }
1810 
1811         int truthRIndex = -1;
1812 
1813         // get which hit radial index this it
1814         for (int k = 0; k < (int) m_truth_RPeaks.size(); k++)
1815         {
1816           if (std::abs(tR - m_truth_RPeaks[k]) < 0.5)
1817           {
1818             truthRIndex = k;
1819             break;
1820           }
1821         }
1822 
1823         if (truthRIndex == -1 || truthRIndex != clustRMatchIndex)
1824         {
1825           truth_index++;
1826           continue;
1827         }
1828 
1829         if ((!side && tZ > 0) || (side && tZ < 0))
1830         {
1831           truth_index++;
1832           continue;
1833         }
1834 
1835         auto dphi = delta_phi(tPhi - rPhi);
1836         if (fabs(dphi) > m_phi_cut)
1837         {
1838           truth_index++;
1839           continue;
1840         }
1841 
1842         float dist = sqrt(pow(tR * sin(tPhi) - rR * sin(rPhi), 2) + pow(tR * cos(tPhi) - rR * cos(rPhi), 2));
1843 
1844         if (dist < reco_distToNN[recoIndex])
1845         {
1846           reco_distToNN[recoIndex] = dist;
1847           truthMatchIndex = truth_index;
1848         }
1849 
1850         truth_index++;
1851 
1852       }  // end of truth loop
1853 
1854       if (!reco_matched[recoIndex])
1855       {
1856         reco_matchedTruthIndex[recoIndex] = truthMatchIndex;
1857       }
1858       recoIndex++;
1859     }
1860   }  // end fancy
1861   else
1862   {
1863     int reco_index = 0;
1864     for (const auto& reco : reco_pos)
1865     {
1866       double rR = get_r(reco.X(), reco.Y());
1867       double rPhi = reco.Phi();
1868       bool side = reco_side[reco_index];
1869 
1870       truth_index = 0;
1871       double minNNDist = 100000.0;
1872       int match_localTruth = -1;
1873       for (const auto& truth : m_truth_pos)
1874       {
1875         // std::cout << "reco_index " << reco_index << "   truth_index " << truth_index << std::endl;
1876         double tR = get_r(truth.X(), truth.Y());
1877         double tPhi = truth.Phi();
1878         double tZ = truth.Z();
1879 
1880         if ((!side && tZ > 0) || (side && tZ < 0))
1881         {
1882           truth_index++;
1883           continue;
1884         }
1885 
1886         auto dR = fabs(tR - rR);
1887         if (dR > 5.0)
1888         {
1889           truth_index++;
1890           continue;
1891         }
1892 
1893         auto dphi = delta_phi(tPhi - rPhi);
1894         if (fabs(dphi) > 0.05)
1895         {
1896           truth_index++;
1897           continue;
1898         }
1899 
1900         double dist = sqrt(pow(truth.X() - reco.X(), 2) + pow(truth.Y() - reco.Y(), 2));
1901         if (dist < minNNDist)
1902         {
1903           minNNDist = dist;
1904           match_localTruth = truth_index;
1905         }
1906         truth_index++;
1907       }  // end truth loop
1908 
1909       if (match_localTruth == -1)
1910       {
1911         reco_index++;
1912         continue;
1913       }
1914 
1915       truth_NNRecoIndex[match_localTruth].push_back(reco_index);
1916       NNDist[reco_index] = sqrt(pow(m_truth_pos[match_localTruth].X() - reco.X(), 2) + pow(m_truth_pos[match_localTruth].Y() - reco.Y(), 2));
1917       NNR[reco_index] = get_r(m_truth_pos[match_localTruth].X(), m_truth_pos[match_localTruth].Y());
1918       NNPhi[reco_index] = m_truth_pos[match_localTruth].Phi();
1919       NNIndex[reco_index] = m_truth_index[match_localTruth];
1920 
1921       reco_index++;
1922     }  // end reco loop
1923 
1924     truth_index = 0;
1925     for (const auto& truthIndex : truth_NNRecoIndex)
1926     {
1927       double maxADC = 0.0;
1928       int recoMatchIndex = -1;
1929 
1930       int currentTruthIndex = m_truth_index[truth_index];
1931       int currentRow = (currentTruthIndex / 100) % 100;
1932 
1933       if (currentRow == 15 || currentRow == 23 || currentRow == 31)
1934       {
1935         truth_index++;
1936         continue;
1937       }
1938 
1939       for (const auto& recoIndex : truthIndex)
1940       {
1941         // if(reco_nLayers[recoIndex] < 2 || reco_nLayers[recoIndex] > 3) continue;
1942         if (reco_nLayers[recoIndex] != 2)
1943         {
1944           continue;
1945         }
1946 
1947         int lowRMatch = 0;
1948         int highRMatch = (int) m_truth_RPeaks.size() - 1;
1949         int upperBound = (int) m_truth_RPeaks.size();
1950         while (lowRMatch <= highRMatch)
1951         {
1952           int mid = lowRMatch + (highRMatch - lowRMatch) / 2;
1953           if (m_truth_RPeaks[mid] >= reco_pos[recoIndex].Perp())
1954           {
1955             upperBound = mid;
1956             highRMatch = mid - 1;
1957           }
1958           else
1959           {
1960             lowRMatch = mid + 1;
1961           }
1962         }
1963         upperBound = std::max(upperBound, 1);
1964 
1965         double dR = m_truth_pos[truth_index].Perp() - reco_pos[recoIndex].Perp();
1966         if (fabs(dR) > 0.5 * (m_truth_RPeaks[upperBound] - m_truth_RPeaks[upperBound - 1]))
1967         {
1968           continue;
1969         }
1970 
1971         double dphi = delta_phi(m_truth_pos[truth_index].Phi() - reco_pos[recoIndex].Phi());
1972         if (fabs(dphi) > 0.5 * phiSpacing[upperBound - 1])
1973         {
1974           continue;
1975         }
1976 
1977         if (reco_adc[recoIndex] > maxADC)
1978         {
1979           maxADC = reco_adc[recoIndex];
1980           recoMatchIndex = recoIndex;
1981         }
1982       }
1983 
1984       if (recoMatchIndex >= 0)
1985       {
1986         truth_matchedRecoIndex[truth_index] = recoMatchIndex;
1987         reco_matched[recoMatchIndex] = true;
1988         reco_matchedTruthIndex[recoMatchIndex] = truth_index;
1989         truth_matched[truth_index] = true;
1990         nMatched++;
1991       }
1992 
1993       truth_index++;
1994     }
1995   }  // end else for fancy
1996 
1997   // print some statistics:
1998   if (Verbosity() > 1)
1999   {
2000     const auto n_valid_truth = std::count_if(m_truth_pos.begin(), m_truth_pos.end(), [](const TVector3& pos)
2001                                              { return get_r(pos.x(), pos.y()) > 30; });
2002     std::cout << "TpcCentralMembraneMatching::process_event - m_truth_pos size: " << m_truth_pos.size() << std::endl;
2003     std::cout << "TpcCentralMembraneMatching::process_event - m_truth_pos size, r>30cm: " << n_valid_truth << std::endl;
2004     std::cout << "TpcCentralMembraneMatching::process_event - reco_pos size: " << reco_pos.size() << std::endl;
2005     std::cout << "TpcCentralMembraneMatching::process_event - matched_pair size: " << nMatched << std::endl;
2006   }
2007 
2008   if (m_savehistograms)
2009   {
2010     for (int i = 0; i < (int) reco_pos.size(); i++)
2011     {
2012       m_matched = reco_matched[i];
2013       m_truthR = m_truth_pos[reco_matchedTruthIndex[i]].Perp();
2014       m_truthPhi = m_truth_pos[reco_matchedTruthIndex[i]].Phi();
2015       m_distanceToTruth = sqrt(pow(m_truth_pos[reco_matchedTruthIndex[i]].Y() - reco_pos[i].Y(), 2) + pow(m_truth_pos[reco_matchedTruthIndex[i]].X() - reco_pos[i].X(), 2));
2016       m_truthIndex = m_truth_index[reco_matchedTruthIndex[i]];
2017       m_recoR = reco_pos[i].Perp();
2018       m_recoPhi = reco_pos[i].Phi();
2019       m_recoZ = reco_pos[i].Z();
2020       m_rawR = raw_pos[i].Perp();
2021       m_rawPhi = raw_pos[i].Phi();
2022       m_staticR = static_pos[i].Perp();
2023       m_staticPhi = static_pos[i].Phi();
2024       m_side = reco_side[i];
2025       m_fitMode = fitMode[i];
2026       m_isSaturated = isSaturated[i];
2027       m_adc = reco_adc[i];
2028       m_nhits = reco_nhits[i];
2029       m_nLayers = reco_nLayers[i];
2030       m_nIPhi = reco_nIPhi[i];
2031       m_nIT = reco_nIT[i];
2032       m_layersSD = reco_SDLayer[i];
2033       m_IPhiSD = reco_SDIPhi[i];
2034       m_ITSD = reco_SDIT[i];
2035       m_layersWeightedSD = reco_SDWeightedLayer[i];
2036       m_IPhiWeightedSD = reco_SDWeightedIPhi[i];
2037       m_ITWeightedSD = reco_SDWeightedIT[i];
2038 
2039       e_matched.push_back(reco_matched[i]);
2040       e_truthIndex.push_back(m_truth_index[reco_matchedTruthIndex[i]]);
2041       e_truthR.push_back(m_truth_pos[reco_matchedTruthIndex[i]].Perp());
2042       e_truthPhi.push_back(m_truth_pos[reco_matchedTruthIndex[i]].Phi());
2043       e_recoR.push_back(reco_pos[i].Perp());
2044       e_recoPhi.push_back(reco_pos[i].Phi());
2045       e_side.push_back(reco_side[i]);
2046       e_isSaturated.push_back(isSaturated[i]);
2047       e_fitMode.push_back(fitMode[i]);
2048       e_NNR.push_back(NNR[i]);
2049       e_NNPhi.push_back(NNPhi[i]);
2050       e_NNIndex.push_back(NNIndex[i]);
2051       e_NNDistance.push_back(NNDist[i]);
2052       e_adc.push_back(reco_adc[i]);
2053 
2054       if (m_side)
2055       {
2056         m_lowShift = m_matchLow[1];
2057         m_highShift = m_matchHigh[1];
2058       }
2059       else
2060       {
2061         m_lowShift = m_matchLow[0];
2062         m_highShift = m_matchHigh[0];
2063       }
2064 
2065       int region = -1;
2066 
2067       if (m_recoR < 41)
2068       {
2069         region = 0;
2070       }
2071       else if (m_recoR >= 41 && m_recoR < 58)
2072       {
2073         region = 1;
2074       }
2075       else if (m_recoR >= 58)
2076       {
2077         region = 2;
2078       }
2079 
2080       if (region == -1)
2081       {
2082         m_phiRotation = -999.0;
2083       }
2084       else
2085       {
2086         m_phiRotation = m_recoRotation[m_side][region];
2087       }
2088 
2089       m_NNDistance = NNDist[i];
2090       m_NNR = NNR[i];
2091       m_NNPhi = NNPhi[i];
2092       m_NNIndex = NNIndex[i];
2093 
2094       match_tree->Fill();
2095     }
2096 
2097     event_tree->Fill();
2098   }
2099 
2100   unsigned int ckey = 0;
2101   for (unsigned int i = 0; i < m_truth_pos.size(); i++)
2102   {
2103     if (!truth_matched[i])
2104     {
2105       continue;
2106     }
2107 
2108     // std::cout << "i=" << i << "   ckey=" << ckey << "   reco_index=" << truth_matchedRecoIndex[i] << std::endl;
2109 
2110     int reco_index = truth_matchedRecoIndex[i];
2111 
2112     if (reco_index == -1)
2113     {
2114       continue;
2115     }
2116 
2117     // std::cout << "good reco_index" << std::endl;
2118 
2119     auto* cmdiff = new CMFlashDifferencev1();
2120     cmdiff->setTruthPhi(m_truth_pos[i].Phi());
2121     cmdiff->setTruthR(m_truth_pos[i].Perp());
2122     cmdiff->setTruthZ(m_truth_pos[i].Z());
2123 
2124     if (m_averageMode)
2125     {
2126       cmdiff->setRecoPhi(static_pos[reco_index].Phi());
2127       cmdiff->setRecoR(static_pos[reco_index].Perp());
2128       cmdiff->setRecoZ(static_pos[reco_index].Z());
2129       cmdiff->setNclusters(reco_nhits[reco_index]);
2130     }
2131     else
2132     {
2133       cmdiff->setRecoPhi(reco_pos[reco_index].Phi());
2134       cmdiff->setRecoR(reco_pos[reco_index].Perp());
2135       cmdiff->setRecoZ(reco_pos[reco_index].Z());
2136       cmdiff->setNclusters(reco_nhits[reco_index]);
2137     }
2138 
2139     if (Verbosity() > 1)
2140     {
2141       std::cout << "adding cmdiff to container with key " << ckey << " in event " << m_event_index << std::endl;
2142     }
2143 
2144     m_cm_flash_diffs->addDifferenceSpecifyKey(ckey, cmdiff);
2145 
2146     // store cluster position
2147     double clus_r = reco_pos[reco_index].Perp();
2148     double clus_phi = reco_pos[reco_index].Phi();
2149     double dr = reco_pos[reco_index].Perp() - m_truth_pos[i].Perp();
2150     double dphi = delta_phi(reco_pos[reco_index].Phi() - m_truth_pos[i].Phi());
2151     if (m_averageMode)
2152     {
2153       clus_r = static_pos[reco_index].Perp();
2154       clus_phi = static_pos[reco_index].Phi();
2155 
2156       dr = static_pos[reco_index].Perp() - m_truth_pos[i].Perp();
2157       dphi = delta_phi(static_pos[reco_index].Phi() - m_truth_pos[i].Phi());
2158     }
2159     if (clus_phi < 0)
2160     {
2161       clus_phi += 2 * M_PI;
2162     }
2163 
2164     // const double clus_z = reco_pos[reco_index].z();
2165     const bool side = reco_side[reco_index];
2166     // const bool side = (clus_z < 0) ? false : true;
2167     //  if(side != reco_side[reco_index]) std::cout << "sides do not match!" << std::endl;
2168 
2169     // calculate residuals (cluster - truth)
2170 
2171     // std::cout << PHWHERE << "   filling graphs on side " << side << " with phi=" << clus_phi << " and R=" << clus_r << "  with dr=" << dr << " and dphi=" << dphi << std::endl;
2172 
2173     gr_dR[side]->AddPoint(clus_phi, clus_r, dr);
2174     gr_dPhi[side]->AddPoint(clus_phi, clus_r, dphi);
2175 
2176     if (clus_phi < M_PI / 12)
2177     {
2178       gr_dR[side]->AddPoint(clus_phi + 2 * M_PI, clus_r, dr);
2179       gr_dPhi[side]->AddPoint(clus_phi + 2 * M_PI, clus_r, dphi);
2180     }
2181     if (clus_phi > 23 * M_PI / 12)
2182     {
2183       gr_dR[side]->AddPoint(clus_phi - 2 * M_PI, clus_r, dr);
2184       gr_dPhi[side]->AddPoint(clus_phi - 2 * M_PI, clus_r, dphi);
2185     }
2186 
2187     // double rdphi = reco_pos[reco_index].Perp() * dphi;
2188 
2189     // std::cout << "about to add to maps for truth index " << m_truth_index[i] << std::endl;
2190 
2191     deltaRByIndex[m_truth_index[i]] += dr;
2192     deltaPhiByIndex[m_truth_index[i]] += dphi;
2193     meanRByIndex[m_truth_index[i]] += clus_r;
2194     meanPhiByIndex[m_truth_index[i]] += clus_phi;
2195     nByIndex[m_truth_index[i]]++;
2196 
2197     // std::cout << "map now has " << nByIndex[m_truth_index[i]] << "entries" << std::endl;
2198 
2199     // currently, we cannot get any z distortion since we don't know when the laser actually flashed
2200     // so the distortion is set to 0 for now
2201     // const double dz = reco_pos[reco_index].z() - m_truth_pos[i].z();
2202     // const double dz = 0.0;
2203 
2204     // fill distortion correction histograms
2205     /*
2206      * TODO:
2207      * - we might need to only fill the histograms for cm clusters that have 2 clusters only
2208      * - we might need a smoothing procedure to fill the bins that have no entries using neighbors
2209      */
2210 
2211     /*
2212     //original method
2213    for (const auto& dcc : {m_dcc_out, m_dcc_out_aggregated.get()})
2214    {
2215      static_cast<TH2*>(dcc->m_hDRint[side])->Fill(clus_phi, clus_r, dr);
2216      static_cast<TH2*>(dcc->m_hDPint[side])->Fill(clus_phi, clus_r, rdphi);
2217      static_cast<TH2*>(dcc->m_hDZint[side])->Fill(clus_phi, clus_r, dz);
2218      static_cast<TH2*>(dcc->m_hentries[side])->Fill(clus_phi, clus_r);
2219    }
2220    */
2221 
2222     ckey++;
2223   }
2224 
2225   // std::cout << "about to fill fluct hist" << std::endl;
2226 
2227   for (int s = 0; s < 2; s++)
2228   {
2229     bool firstGoodR = false;
2230     for (int j = 1; j <= m_dcc_out->m_hDRint[s]->GetNbinsY(); j++)
2231     {
2232       double RVal = m_dcc_out->m_hDRint[s]->GetYaxis()->GetBinCenter(j);
2233       double Rlow = m_dcc_out->m_hDRint[s]->GetYaxis()->GetBinLowEdge(j);
2234       double Rhigh = m_dcc_out->m_hDRint[s]->GetYaxis()->GetBinLowEdge(j + 1);
2235 
2236       if (!firstGoodR)
2237       {
2238         for (int p = 0; p < gr_dR[s]->GetN(); p++)
2239         {
2240           if (gr_dR[s]->GetY()[p] > Rlow && gr_dR[s]->GetY()[p] > Rhigh)
2241           {
2242             firstGoodR = true;
2243             break;
2244           }
2245         }
2246         continue;
2247       }
2248 
2249       for (int i = 2; i <= m_dcc_out->m_hDRint[s]->GetNbinsX() - 1; i++)
2250       {
2251         double phiVal = m_dcc_out->m_hDRint[s]->GetXaxis()->GetBinCenter(i);
2252         m_dcc_out->m_hDRint[s]->SetBinContent(i, j, gr_dR[s]->Interpolate(phiVal, RVal));
2253         m_dcc_out->m_hDPint[s]->SetBinContent(i, j, RVal * gr_dPhi[s]->Interpolate(phiVal, RVal));
2254       }
2255     }
2256   }
2257 
2258   if (Verbosity() > 1)
2259   {
2260     std::cout << "TpcCentralMembraneMatching::process_events - cmclusters: " << m_corrected_CMcluster_map->size() << std::endl;
2261     std::cout << "TpcCentralMembraneMatching::process_events - matched pairs: " << nMatched << std::endl;
2262     std::cout << "TpcCentralMembraneMatching::process_events - differences: " << m_cm_flash_diffs->size() << std::endl;
2263     std::cout << "TpcCentralMembraneMatching::process_events - entries: " << m_dcc_out->m_hentries[0]->GetEntries() << ", " << m_dcc_out->m_hentries[1]->GetEntries() << std::endl;
2264   }
2265 
2266   // normalize per-event distortion correction histograms and fill guarding bins
2267   // normalize_distortions(m_dcc_out);
2268   // fill_guarding_bins(m_dcc_out);
2269 
2270   if (Verbosity() > 2)
2271   {
2272     // read back differences from node tree as a check
2273     auto diffrange = m_cm_flash_diffs->getDifferences();
2274     for (auto cmitr = diffrange.first;
2275          cmitr != diffrange.second;
2276          ++cmitr)
2277     {
2278       auto key = cmitr->first;
2279       auto* cmreco = cmitr->second;
2280 
2281       std::cout << " key " << key
2282                 << " nclus " << cmreco->getNclusters()
2283                 << " truth Phi " << cmreco->getTruthPhi() << " reco Phi " << cmreco->getRecoPhi()
2284                 << " truth R " << cmreco->getTruthR() << " reco R " << cmreco->getRecoR()
2285                 << " truth Z " << cmreco->getTruthZ() << " reco Z " << cmreco->getRecoZ()
2286                 << std::endl;
2287     }
2288   }
2289 
2290   if (!m_useHeader)
2291   {
2292     m_event_index++;
2293   }
2294 
2295   return Fun4AllReturnCodes::EVENT_OK;
2296 }
2297 
2298 //____________________________________________________________________________..
2299 int TpcCentralMembraneMatching::End(PHCompositeNode* /*topNode*/)
2300 {
2301   std::cout << PHWHERE << "starting TpcCentralMembraneMatching::End()" << std::endl;
2302 
2303   // write distortion corrections
2304   if (m_dcc_out_aggregated)
2305   {
2306     // normalize aggregated distortion correction histograms and fill guarding bins
2307     /*
2308     if(m_doHadd)
2309     {
2310       normalize_distortions(m_dcc_out_aggregated.get());
2311     }
2312     fill_guarding_bins(m_dcc_out_aggregated.get());
2313     */
2314 
2315     for (int s = 0; s < 2; s++)
2316     {
2317       gr_dR[s]->Clear();
2318       gr_dPhi[s]->Clear();
2319     }
2320 
2321     for (auto [idx, n] : nByIndex)
2322     {
2323       if (n < 50)
2324       {
2325         continue;
2326       }
2327       int side = (idx < 180000 ? 1 : 0);
2328 
2329       gr_dR[side]->AddPoint(meanPhiByIndex[idx] / n, meanRByIndex[idx] / n, deltaRByIndex[idx] / n);
2330       gr_dPhi[side]->AddPoint(meanPhiByIndex[idx] / n, meanRByIndex[idx] / n, deltaPhiByIndex[idx] / n);
2331       gr_points[side]->AddPoint(meanPhiByIndex[idx] / n, meanRByIndex[idx] / n);
2332 
2333       if (meanPhiByIndex[idx] / n < M_PI / 12)
2334       {
2335         gr_dR[side]->AddPoint(meanPhiByIndex[idx] / n + 2 * M_PI, meanRByIndex[idx] / n, deltaRByIndex[idx] / n);
2336         gr_dPhi[side]->AddPoint(meanPhiByIndex[idx] / n + 2 * M_PI, meanRByIndex[idx] / n, deltaPhiByIndex[idx] / n);
2337       }
2338       if (meanPhiByIndex[idx] / n > 23 * M_PI / 12)
2339       {
2340         gr_dR[side]->AddPoint(meanPhiByIndex[idx] / n - 2 * M_PI, meanRByIndex[idx] / n, deltaRByIndex[idx] / n);
2341         gr_dPhi[side]->AddPoint(meanPhiByIndex[idx] / n - 2 * M_PI, meanRByIndex[idx] / n, deltaPhiByIndex[idx] / n);
2342       }
2343     }
2344 
2345     for (int s = 0; s < 2; s++)
2346     {
2347       bool firstGoodR = false;
2348       for (int j = 1; j <= m_dcc_out_aggregated->m_hDRint[s]->GetNbinsY(); j++)
2349       {
2350         double RVal = m_dcc_out_aggregated->m_hDRint[s]->GetYaxis()->GetBinCenter(j);
2351         double Rlow = m_dcc_out_aggregated->m_hDRint[s]->GetYaxis()->GetBinLowEdge(j);
2352         double Rhigh = m_dcc_out_aggregated->m_hDRint[s]->GetYaxis()->GetBinLowEdge(j + 1);
2353 
2354         if (!firstGoodR)
2355         {
2356           for (int p = 0; p < gr_dR[s]->GetN(); p++)
2357           {
2358             if (gr_dR[s]->GetY()[p] > Rlow && gr_dR[s]->GetY()[p] > Rhigh)
2359             {
2360               firstGoodR = true;
2361               break;
2362             }
2363           }
2364           continue;
2365         }
2366 
2367         for (int i = 2; i <= m_dcc_out_aggregated->m_hDRint[s]->GetNbinsX() - 1; i++)
2368         {
2369           double phiVal = m_dcc_out_aggregated->m_hDRint[s]->GetXaxis()->GetBinCenter(i);
2370 
2371           m_dcc_out_aggregated->m_hDRint[s]->SetBinContent(i, j, gr_dR[s]->Interpolate(phiVal, RVal));
2372           m_dcc_out_aggregated->m_hDPint[s]->SetBinContent(i, j, RVal * gr_dPhi[s]->Interpolate(phiVal, RVal));
2373         }
2374       }
2375     }
2376 
2377     // create TFile and write all histograms
2378     std::unique_ptr<TFile> outputfile(TFile::Open(m_outputfile.c_str(), "RECREATE"));
2379     outputfile->cd();
2380 
2381     // loop over side
2382     for (unsigned int i = 0; i < 2; ++i)
2383     {
2384       for (const auto& h : {m_dcc_out_aggregated->m_hDRint[i], m_dcc_out_aggregated->m_hDPint[i], m_dcc_out_aggregated->m_hDZint[i], m_dcc_out_aggregated->m_hentries[i]})
2385       {
2386         if (h)
2387         {
2388           std::cout << "writing in " << m_outputfile << "   histogram " << h->GetName() << std::endl;
2389           h->Write();
2390         }
2391       }
2392       // gr_points[i]->Write(Form("gr_points_%sz",(i==1 ? "pos" : "neg")));
2393       // gr_dR[i]->Write(Form("gr_dr_%sz",(i==1 ? "pos" : "neg")));
2394       // gr_dPhi[i]->Write(Form("gr_dPhi_%sz",(i==1 ? "pos" : "neg")));
2395       gr_points[i]->Write(std::format("gr_points_{}z", (i == 1 ? "pos" : "neg")).c_str());
2396       gr_dR[i]->Write(std::format("gr_dr_{}z", (i == 1 ? "pos" : "neg")).c_str());
2397       gr_dPhi[i]->Write(std::format("gr_dPhi_{}z", (i == 1 ? "pos" : "neg")).c_str());
2398     }
2399   }
2400 
2401   // write evaluation histograms
2402   if (m_savehistograms && fout)
2403   {
2404     fout->cd();
2405 
2406     hxy_reco->Write();
2407     hxy_truth->Write();
2408     hdrdphi->Write();
2409     hrdr->Write();
2410     hrdphi->Write();
2411     hdphi->Write();
2412     hdrphi->Write();
2413     hdr1_single->Write();
2414     hdr2_single->Write();
2415     hdr3_single->Write();
2416     hdr1_double->Write();
2417     hdr2_double->Write();
2418     hdr3_double->Write();
2419     hnclus->Write();
2420 
2421     fout->Close();
2422   }
2423 
2424   if (m_savehistograms && m_debugfile)
2425   {
2426     m_debugfile->cd();
2427 
2428     std::cout << "writing in " << m_debugfilename << "   match tree " << std::endl;
2429 
2430     /*
2431     match_tree->Write();
2432     // match_ntup->Write();
2433     std::cout << "writing in " << m_debugfilename << "   truth_r_phi " << std::endl;
2434     truth_r_phi[0]->Write();
2435     truth_r_phi[1]->Write();
2436     std::cout << "writing in " << m_debugfilename << "   reco_r_phi " << std::endl;
2437     reco_r_phi[0]->Write();
2438     reco_r_phi[1]->Write();
2439     std::cout << "writing in " << m_debugfilename << "   m_matchResiduals " << std::endl;
2440     m_matchResiduals[0]->Write();
2441     m_matchResiduals[1]->Write();
2442     */
2443     m_debugfile->Write();
2444     std::cout << "closing " << m_debugfilename << std::endl;
2445     m_debugfile->Close();
2446     std::cout << m_debugfilename << " has been closed" << std::endl;
2447   }
2448 
2449   return Fun4AllReturnCodes::EVENT_OK;
2450 }
2451 
2452 //____________________________________________________________________________..
2453 
2454 int TpcCentralMembraneMatching::GetNodes(PHCompositeNode* topNode)
2455 {
2456   //---------------------------------
2457   // Get Objects off of the Node Tree
2458   //---------------------------------
2459 
2460   // m_corrected_CMcluster_map  = findNode::getClass<CMFlashClusterContainer>(topNode, "CORRECTED_CM_CLUSTER");
2461   m_corrected_CMcluster_map = findNode::getClass<LaserClusterContainer>(topNode, "LASER_CLUSTER");
2462   if (!m_corrected_CMcluster_map)
2463   {
2464     std::cout << PHWHERE << "CORRECTED_CM_CLUSTER Node missing, abort." << std::endl;
2465     return Fun4AllReturnCodes::ABORTRUN;
2466   }
2467 
2468   // input tpc distortion correction module edge
2469   m_dcc_in_module_edge = findNode::getClass<TpcDistortionCorrectionContainer>(topNode, "TpcDistortionCorrectionContainerModuleEdge");
2470   if (m_dcc_in_module_edge)
2471   {
2472     std::cout << "TpcCentralMembraneMatching::GetNodes - found TPC distortion correction container module edge" << std::endl;
2473   }
2474 
2475   // input tpc distortion correction static
2476   m_dcc_in_static = findNode::getClass<TpcDistortionCorrectionContainer>(topNode, "TpcDistortionCorrectionContainerStatic");
2477   if (m_dcc_in_static)
2478   {
2479     std::cout << "TpcCentralMembraneMatching::GetNodes - found TPC distortion correction container static" << std::endl;
2480   }
2481 
2482   // input tpc distortion correction average
2483   m_dcc_in_average = findNode::getClass<TpcDistortionCorrectionContainer>(topNode, "TpcDistortionCorrectionContainerAverage");
2484   if (m_dcc_in_average)
2485   {
2486     std::cout << "TpcCentralMembraneMatching::GetNodes - found TPC distortion correction container average" << std::endl;
2487   }
2488 
2489   PHNodeIterator iter(topNode);
2490 
2491   // Looking for the DST node
2492   PHCompositeNode* dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
2493   if (!dstNode)
2494   {
2495     std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
2496     return Fun4AllReturnCodes::ABORTRUN;
2497   }
2498 
2499   // Looking for the RUN node
2500   // PHCompositeNode *runNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "RUN"));
2501   // if (!runNode)
2502   //{
2503   // std::cout << PHWHERE << "RUN Node missing, doing nothing." << std::endl;
2504   // return Fun4AllReturnCodes::ABORTRUN;
2505   //}
2506   // PHNodeIterator runiter(runNode);
2507   // auto flashDiffContainer = findNode::getClass<CMFlashDifferenceContainerv1>(runNode, "CM_FLASH_DIFFERENCES");
2508   auto* flashDiffContainer = findNode::getClass<CMFlashDifferenceContainerv1>(topNode, "CM_FLASH_DIFFERENCES");
2509   if (!flashDiffContainer)
2510   {
2511     PHNodeIterator dstiter(dstNode);
2512     PHCompositeNode* DetNode =
2513         dynamic_cast<PHCompositeNode*>(dstiter.findFirst("PHCompositeNode", "TRKR"));
2514     if (!DetNode)
2515     {
2516       DetNode = new PHCompositeNode("TRKR");
2517       dstNode->addNode(DetNode);
2518     }
2519 
2520     flashDiffContainer = new CMFlashDifferenceContainerv1;
2521     PHIODataNode<PHObject>* CMFlashDifferenceNode =
2522         new PHIODataNode<PHObject>(flashDiffContainer, "CM_FLASH_DIFFERENCES", "PHObject");
2523     // runNode->addNode(CMFlashDifferenceNode);
2524     DetNode->addNode(CMFlashDifferenceNode);
2525   }
2526 
2527   // m_cm_flash_diffs = findNode::getClass<CMFlashDifferenceContainerv1>(runNode, "CM_FLASH_DIFFERENCES");
2528   m_cm_flash_diffs = findNode::getClass<CMFlashDifferenceContainerv1>(topNode, "CM_FLASH_DIFFERENCES");
2529   if (!m_cm_flash_diffs)
2530   {
2531     std::cout << PHWHERE << " ERROR: Can't find CM_FLASH_DIFFERENCES." << std::endl;
2532     return Fun4AllReturnCodes::ABORTRUN;
2533   }
2534 
2535   /*
2536   // Looking for the DST node
2537   PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
2538   if (!dstNode)
2539     {
2540       std::cout << PHWHERE << "DST Node missing, doing nothing." << std::endl;
2541       return Fun4AllReturnCodes::ABORTRUN;
2542     }
2543   PHNodeIterator dstiter(dstNode);
2544   PHCompositeNode *DetNode =
2545     dynamic_cast<PHCompositeNode *>(dstiter.findFirst("PHCompositeNode", "TRKR"));
2546   if (!DetNode)
2547     {
2548       DetNode = new PHCompositeNode("TRKR");
2549       dstNode->addNode(DetNode);
2550     }
2551 
2552   m_cm_flash_diffs = new CMFlashDifferenceContainerv1;
2553   PHIODataNode<PHObject> *CMFlashDifferenceNode =
2554     new PHIODataNode<PHObject>(m_cm_flash_diffs, "CM_FLASH_DIFFERENCES", "PHObject");
2555   DetNode->addNode(CMFlashDifferenceNode);
2556   */
2557 
2558   //// output tpc fluctuation distortion container
2559   //// this one is filled on the fly on a per-CM-event basis, and applied in the tracking chain
2560   const std::string dcc_out_node_name = "TpcDistortionCorrectionContainerFluctuation";
2561   m_dcc_out = findNode::getClass<TpcDistortionCorrectionContainer>(topNode, dcc_out_node_name);
2562   if (!m_dcc_out)
2563   {
2564     /// Get the RUN node and check
2565     auto* runNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "RUN"));
2566     if (!runNode)
2567     {
2568       std::cout << "TpcCentralMembraneMatching::InitRun - RUN Node missing, quitting" << std::endl;
2569       return Fun4AllReturnCodes::ABORTRUN;
2570     }
2571 
2572     std::cout << "TpcCentralMembraneMatching::GetNodes - creating TpcDistortionCorrectionContainer in node " << dcc_out_node_name << std::endl;
2573     m_dcc_out = new TpcDistortionCorrectionContainer;
2574     m_dcc_out->m_dimensions = 2;
2575     m_dcc_out->m_phi_hist_in_radians = false;
2576     m_dcc_out->m_interpolate_z = true;
2577     auto* node = new PHDataNode<TpcDistortionCorrectionContainer>(m_dcc_out, dcc_out_node_name);
2578     runNode->addNode(node);
2579   }
2580 
2581   // create per event distortions. Do not put on the node tree
2582   // m_dcc_out = new TpcDistortionCorrectionContainer;
2583 
2584   // also prepare the local distortion container, used to aggregate multple events
2585   m_dcc_out_aggregated.reset(new TpcDistortionCorrectionContainer);
2586 
2587   // compute axis limits to include guarding bins, needed for TH2::Interpolate to work
2588   const float phiMin = m_phiMin - (m_phiMax - m_phiMin) / m_phibins;
2589   const float phiMax = m_phiMax + (m_phiMax - m_phiMin) / m_phibins;
2590 
2591   const float rMin = m_rMin - (m_rMax - m_rMin) / m_rbins;
2592   const float rMax = m_rMax + (m_rMax - m_rMin) / m_rbins;
2593 
2594   /*
2595   double r_bins_mm[69] = {217.83-2,217.83,
2596                        223.83, 229.83, 235.83, 241.83, 247.83, 253.83, 259.83, 265.83, 271.83, 277.83, 283.83, 289.83, 295.83, 301.83, 306.83,
2597                        311.05, 317.92, 323.31, 329.27, 334.63, 340.59, 345.95, 351.91, 357.27, 363.23, 368.59, 374.55, 379.91, 385.87, 391.23, 397.19, 402.49,
2598                        411.53, 421.70, 431.90, 442.11, 452.32, 462.52, 472.73, 482.94, 493.14, 503.35, 513.56, 523.76, 533.97, 544.18, 554.39, 564.59, 574.76,
2599                        583.67, 594.59, 605.57, 616.54, 627.51, 638.48, 649.45, 660.42, 671.39, 682.36, 693.33, 704.30, 715.27, 726.24, 737.21, 748.18, 759.11, 759.11+2};
2600 
2601   double r_bins[69];
2602 
2603   for(int i=0; i<69; i++){
2604     r_bins[i] = r_bins_mm[i]/10.0;
2605   }
2606 
2607 
2608 
2609   double phiBins[206] = { 0., 6.3083-2 * M_PI, 6.3401-2 * M_PI, 6.372-2 * M_PI, 6.4039-2 * M_PI, 6.4358-2 * M_PI, 6.4676-2 * M_PI, 6.4995-2 * M_PI, 6.5314-2 * M_PI,
2610                           0.2618, 0.2937, 0.3256, 0.3574, 0.3893, 0.4212, 0.453, 0.4849, 0.5168, 0.5487, 0.5805, 0.6124, 0.6443, 0.6762, 0.7081,
2611                           0.7399, 0.7718, 0.7854, 0.8173, 0.8491, 0.881, 0.9129, 0.9448, 0.9767, 1.0085, 1.0404, 1.0723, 1.1041, 1.136, 1.1679,
2612                           1.1998, 1.2317, 1.2635, 1.2954, 1.309, 1.3409, 1.3727, 1.4046, 1.4365, 1.4684, 1.5002, 1.5321, 1.564, 1.5959, 1.6277,
2613                           1.6596, 1.6915, 1.7234, 1.7552, 1.7871, 1.819, 1.8326, 1.8645, 1.8963, 1.9282, 1.9601, 1.992, 2.0238, 2.0557, 2.0876,
2614                           2.1195, 2.1513, 2.1832, 2.2151, 2.247, 2.2788, 2.3107, 2.3426, 2.3562, 2.3881, 2.42, 2.4518, 2.4837, 2.5156, 2.5474,
2615                           2.5793, 2.6112, 2.6431, 2.6749, 2.7068, 2.7387, 2.7706, 2.8024, 2.8343, 2.8662, 2.8798, 2.9117, 2.9436, 2.9754, 3.0073,
2616                           3.0392, 3.0711, 3.1029, 3.1348, 3.1667, 3.1986, 3.2304, 3.2623, 3.2942, 3.326, 3.3579, 3.3898, 3.4034, 3.4353, 3.4671,
2617                           3.499, 3.5309, 3.5628, 3.5946, 3.6265, 3.6584, 3.6903, 3.7221, 3.754, 3.7859, 3.8178, 3.8496, 3.8815, 3.9134, 3.927,
2618                           3.9589, 3.9907, 4.0226, 4.0545, 4.0864, 4.1182, 4.1501, 4.182, 4.2139, 4.2457, 4.2776, 4.3095, 4.3414, 4.3732, 4.4051,
2619                           4.437, 4.4506, 4.4825, 4.5143, 4.5462, 4.5781, 4.61, 4.6418, 4.6737, 4.7056, 4.7375, 4.7693, 4.8012, 4.8331, 4.865,
2620                           4.8968, 4.9287, 4.9606, 4.9742, 5.0061, 5.0379, 5.0698, 5.1017, 5.1336, 5.1654, 5.1973, 5.2292, 5.2611, 5.2929, 5.3248,
2621                           5.3567, 5.3886, 5.4204, 5.4523, 5.4842, 5.4978, 5.5297, 5.5615, 5.5934, 5.6253, 5.6572, 5.689, 5.7209, 5.7528, 5.7847,
2622                           5.8165, 5.8484, 5.8803, 5.9122, 5.944, 5.9759, 6.0078, 6.0214, 6.0533, 6.0851, 6.117, 6.1489, 6.1808, 6.2127, 6.2445,
2623                           6.2764, 2 * M_PI};
2624   */
2625 
2626   // reset all output distortion container so that they match the requested grid size
2627   const std::array<const std::string, 2> extension = {{"_negz", "_posz"}};
2628   for (const auto& dcc : {m_dcc_out, m_dcc_out_aggregated.get()})
2629   {
2630     // set dimensions to 2, since central membrane flashes only provide distortions at z = 0
2631     dcc->m_dimensions = 2;
2632 
2633     // create all histograms
2634     for (int i = 0; i < 2; ++i)
2635     {
2636       delete dcc->m_hDPint[i];
2637       dcc->m_hDPint[i] = new TH2F(std::format("hIntDistortionP{}", extension[i]).c_str(), std::format("hIntDistortionP{}", extension[i]).c_str(), m_phibins + 2, phiMin, phiMax, m_rbins + 2, rMin, rMax);
2638       delete dcc->m_hDRint[i];
2639       dcc->m_hDRint[i] = new TH2F(std::format("hIntDistortionR{}", extension[i]).c_str(), std::format("hIntDistortionR{}", extension[i]).c_str(), m_phibins + 2, phiMin, phiMax, m_rbins + 2, rMin, rMax);
2640       delete dcc->m_hDZint[i];
2641       dcc->m_hDZint[i] = new TH2F(std::format("hIntDistortionZ{}", extension[i]).c_str(), std::format("hIntDistortionZ{}", extension[i]).c_str(), m_phibins + 2, phiMin, phiMax, m_rbins + 2, rMin, rMax);
2642       delete dcc->m_hentries[i];
2643       dcc->m_hentries[i] = new TH2I(std::format("hEntries{}", extension[i]).c_str(), std::format("hEntries{}", extension[i]).c_str(), m_phibins + 2, phiMin, phiMax, m_rbins + 2, rMin, rMax);
2644 
2645       // delete dcc->m_hDPint[i]; dcc->m_hDPint[i] = new TH2F( std::format("hIntDistortionP{}", extension[i]).c_str(), std::format("hIntDistortionP{}", extension[i]).c_str(), 205, phiBins, 68, r_bins );
2646       // delete dcc->m_hDRint[i]; dcc->m_hDRint[i] = new TH2F( std::format("hIntDistortionR{}", extension[i]).c_str(), std::format("hIntDistortionR{}", extension[i]).c_str(), 205, phiBins, 68, r_bins );
2647       // delete dcc->m_hDZint[i]; dcc->m_hDZint[i] = new TH2F( std::format("hIntDistortionZ{}", extension[i]).c_str(), std::format("hIntDistortionZ{}", extension[i]).c_str(), 205, phiBins, 68, r_bins );
2648       // delete dcc->m_hentries[i]; dcc->m_hentries[i] = new TH2I( std::format("hEntries{}", extension[i]).c_str(), std::format("hEntries{}", extension[i]).c_str(), 205, phiBins, 68, r_bins);
2649     }
2650   }
2651 
2652   return Fun4AllReturnCodes::EVENT_OK;
2653 }
2654 
2655 //_____________________________________________________________
2656 void TpcCentralMembraneMatching::CalculateCenters(
2657     int nPads,
2658     const std::array<double, nRadii>& R,
2659     std::array<int, nRadii>& nGoodStripes,
2660     const std::array<int, nRadii>& keepUntil,
2661     std::array<int, nRadii>& nStripesIn,
2662     std::array<int, nRadii>& nStripesBefore,
2663     double cx[][nRadii], double cy[][nRadii])
2664 {
2665   const double phi_module = M_PI / 6.0;  // angle span of a module
2666   const int pr_mult = 3;                 // multiples of intrinsic resolution of pads
2667   const int dw_mult = 8;                 // multiples of diffusion width
2668   const double diffwidth = 0.6 * mm;     // diffusion width
2669   const double adjust = 0.015;           // arbitrary angle to center the pattern in a petal
2670 
2671   double theta = 0.0;
2672 
2673   // center coords
2674 
2675   // calculate spacing first:
2676   std::array<double, nRadii> spacing{};
2677   for (int i = 0; i < nRadii; i++)
2678   {
2679     spacing[i] = 2.0 * ((dw_mult * diffwidth / R[i]) + (pr_mult * phi_module / nPads));
2680   }
2681 
2682   // center calculation
2683   for (int j = 0; j < nRadii; j++)
2684   {
2685     int i_out = 0;
2686     for (int i = keepThisAndAfter[j]; i < keepUntil[j]; i++)
2687     {
2688       if (j % 2 == 0)
2689       {
2690         theta = i * spacing[j] + (spacing[j] / 2) - adjust;
2691         cx[i_out][j] = R[j] * cos(theta) / cm;
2692         cy[i_out][j] = R[j] * sin(theta) / cm;
2693       }
2694       else
2695       {
2696         theta = (i + 1) * spacing[j] - adjust;
2697         cx[i_out][j] = R[j] * cos(theta) / cm;
2698         cy[i_out][j] = R[j] * sin(theta) / cm;
2699       }
2700 
2701       if (Verbosity() > 2)
2702       {
2703         std::cout << " j " << j << " i " << i << " i_out " << i_out << " theta " << theta << " cx " << cx[i_out][j] << " cy " << cy[i_out][j]
2704                   << " radius " << sqrt(pow(cx[i_out][j], 2) + pow(cy[i_out][j], 2)) << std::endl;
2705       }
2706 
2707       i_out++;
2708 
2709       nStripesBefore_R1_e[0] = 0;
2710 
2711       nStripesIn[j] = keepUntil[j] - keepThisAndAfter[j];
2712       if (j == 0)
2713       {
2714         nStripesBefore[j] = 0;
2715       }
2716       else
2717       {
2718         nStripesBefore[j] = nStripesIn[j - 1] + nStripesBefore[j - 1];
2719       }
2720       nStripesBefore_R1_e[0] = 0;
2721     }
2722     nGoodStripes[j] = i_out;
2723   }
2724 }