36#include <fairlogger/Logger.h>
64 mPreClusterFinder.
init();
69 mADCToCharge = [](uint32_t
adc) {
72 return static_cast<double>(
charge);
76 mLowestPadCharge = 4.f * 0.22875f;
77 mLowestPixelCharge = mLowestPadCharge / 12.;
78 mLowestClusterCharge = 2. * mLowestPadCharge;
81 mMathiesons[0].setPitch(0.21);
82 mMathiesons[0].setSqrtKx3AndDeriveKx2Kx4(0.7000);
83 mMathiesons[0].setSqrtKy3AndDeriveKy2Ky4(0.7550);
86 mMathiesons[1].setPitch(0.25);
87 mMathiesons[1].setSqrtKx3AndDeriveKx2Kx4(0.7131);
88 mMathiesons[1].setSqrtKy3AndDeriveKy2Ky4(0.7642);
94 mLowestPixelCharge = mLowestPadCharge / 12.;
95 mLowestClusterCharge = 2. * mLowestPadCharge;
113 mPreClusterFinder.
deinit();
136 mMathieson = (
digits[0].getDetID() < 300) ? &mMathiesons[0] : &mMathiesons[1];
142 std::vector<int> removedDigits{};
143 simplifyPreCluster(removedDigits);
145 if (mPreCluster->multiplicity() > 1) {
148 int iNewCluster = mClusters.size();
151 if (mClusters.size() > iNewCluster) {
154 int iFirstNewDigit = mUsedDigits.size();
155 for (
const auto& pad : *mPreCluster) {
157 mUsedDigits.emplace_back(
digits[pad.digitIndex()]);
160 int nNewDigits = mUsedDigits.size() - iFirstNewDigit;
163 for (; iNewCluster < mClusters.size(); ++iNewCluster) {
164 mClusters[iNewCluster].uid = Cluster::buildUniqueId(
digits[0].getDetID() / 100 - 1,
digits[0].getDetID(), iNewCluster);
165 mClusters[iNewCluster].firstDigit = iFirstNewDigit;
166 mClusters[iNewCluster].nDigits = nNewDigits;
167 setClusterResolution(mClusters[iNewCluster]);
172 if (!removedDigits.empty()) {
175 mPreClusterFinder.
reset();
176 for (
auto iDigit : removedDigits) {
181 std::vector<PreCluster> preClusters{};
182 std::vector<Digit> usedDigits{};
183 int nPreClusters = mPreClusterFinder.
run();
184 preClusters.reserve(nPreClusters);
185 usedDigits.reserve(removedDigits.size());
189 for (
const auto& preCluster : preClusters) {
190 findClusters({&usedDigits[preCluster.firstDigit], preCluster.nDigits});
196void ClusterFinderOriginal::resetPreCluster(gsl::span<const Digit>&
digits)
200 mPreCluster->clear();
204 for (
int iDigit = 0; iDigit <
digits.size(); ++iDigit) {
206 const auto& digit =
digits[iDigit];
207 int padID = digit.getPadID();
211 double dx = mSegmentation->
padSizeX(padID) / 2.;
212 double dy = mSegmentation->
padSizeY(padID) / 2.;
213 double charge = mADCToCharge(digit.getADC());
214 bool isSaturated = digit.isSaturated();
218 throw std::runtime_error(
"The precluster contains a digit with charge <= 0");
226void ClusterFinderOriginal::simplifyPreCluster(std::vector<int>& removedDigits)
234 if (mPreCluster->multiplicity() < 3 && mPreCluster->charge() < mLowestClusterCharge) {
235 mPreCluster->clear();
240 if (mPreCluster->multiplicity(0) == 0 || mPreCluster->multiplicity(1) == 0) {
245 std::vector<bool> overlap(mPreCluster->multiplicity(),
false);
246 for (
int i = 0;
i < mPreCluster->multiplicity(); ++
i) {
247 const auto& padi = mPreCluster->pad(
i);
248 for (
int j =
i + 1;
j < mPreCluster->multiplicity(); ++
j) {
249 const auto& padj = mPreCluster->pad(
j);
250 if (padi.plane() == padj.plane() || (overlap[
i] && overlap[
j])) {
261 int nNotOverlapping(0);
262 for (
int i = 0;
i < mPreCluster->multiplicity(); ++
i) {
270 if (nNotOverlapping > 0) {
271 const mapping::CathodeSegmentation* cathSeg[2] = {&mSegmentation->
bending(), &mSegmentation->
nonBending()};
272 for (
int i = mPreCluster->multiplicity() - 1;
i >= 0; --
i) {
276 const auto& pad = mPreCluster->pad(
i);
277 if (nNotOverlapping == 1 && pad.charge() < mLowestPadCharge) {
280 int cathPadIdx = cathSeg[1 - pad.plane()]->findPadByPosition(pad.x(), pad.y());
281 if (!cathSeg[1 - pad.plane()]->isValid(cathPadIdx)) {
284 removedDigits.push_back(pad.digitIndex());
285 mPreCluster->removePad(
i);
290 if (!mPreCluster->isSaturated() && mPreCluster->chargeAsymmetry() > 0.5) {
293 int plane = mPreCluster->maxChargePlane();
294 double chargeMin(std::numeric_limits<double>::max()), chargeMax(-1.);
295 int iPadMin(-1), iPadMax(-1);
296 for (
int i = 0;
i < mPreCluster->multiplicity(); ++
i) {
297 const auto& pad = mPreCluster->pad(
i);
298 if (pad.plane() == plane) {
299 if (pad.charge() < chargeMin) {
300 chargeMin = pad.charge();
303 if (pad.charge() > chargeMax) {
304 chargeMax = pad.charge();
309 if (iPadMin < 0 || iPadMax < 0) {
310 throw std::runtime_error(
"This plane should contain at least 1 pad!?");
314 const auto& padMin = mPreCluster->pad(iPadMin);
315 const auto& padMax = mPreCluster->pad(iPadMax);
316 double dxOfMin = (padMin.x() - padMax.x()) / padMax.dx() / 2.;
317 double dyOfMin = (padMin.y() - padMax.y()) / padMax.dy() / 2.;
318 double distOfMin = TMath::Sqrt(dxOfMin * dxOfMin + dyOfMin * dyOfMin);
321 double precision = SDistancePrecision / 2. * TMath ::Sqrt((1. / padMax.dx() / padMax.dx() + 1. / padMax.dy() / padMax.dy()) / 2.);
323 std::multimap<double,
int,
decltype(
cmp)> distIndices(
cmp);
324 for (
int i = 0;
i < mPreCluster->multiplicity(); ++
i) {
326 distIndices.emplace(0.,
i);
328 const auto& pad = mPreCluster->pad(
i);
329 if (pad.plane() == plane) {
330 double dx = (pad.x() - padMax.x()) / padMax.dx() / 2.;
331 double dy = (pad.y() - padMax.y()) / padMax.dy() / 2.;
332 distIndices.emplace(TMath::Sqrt(dx * dx + dy * dy),
i);
338 double previousDist(std::numeric_limits<float>::max());
339 double previousChargeMax(-1.);
340 std::set<int, std::greater<>> padsToRemove{};
341 for (
const auto& distIndex : distIndices) {
342 const auto& pad = mPreCluster->pad(distIndex.second);
345 if (distIndex.first > distOfMin +
precision) {
346 double ddx = (pad.x() - padMax.x()) / padMax.dx() / 2. * dxOfMin;
347 double ddy = (pad.y() - padMax.y()) / padMax.dy() / 2. * dyOfMin;
356 if (distIndex.first > previousDist + 1. +
precision) {
361 if (TMath::Abs(distIndex.first - previousDist) >=
precision) {
362 previousChargeMax = chargeMax;
366 if (pad.charge() <= previousChargeMax) {
367 if (TMath::Abs(distIndex.first - previousDist) <
precision) {
368 chargeMax = TMath::Max(pad.charge(), chargeMax);
370 chargeMax = pad.charge();
372 previousDist = distIndex.first;
373 padsToRemove.insert(distIndex.second);
378 for (
auto iPad : padsToRemove) {
379 removedDigits.push_back(mPreCluster->pad(iPad).digitIndex());
380 mPreCluster->removePad(iPad);
386void ClusterFinderOriginal::processPreCluster()
391 if (mPixels.empty()) {
397 if (nPadsXY.first < 4 && nPadsXY.second < 4) {
405 std::unique_ptr<TH2D> histAnode(
nullptr);
406 std::multimap<double, std::pair<int, int>, std::greater<>> localMaxima{};
407 findLocalMaxima(histAnode, localMaxima);
408 if (localMaxima.empty()) {
412 if (localMaxima.size() == 1 || mPreCluster->multiplicity() <= 50) {
420 for (
const auto& localMaximum : localMaxima) {
423 restrictPreCluster(*histAnode, localMaximum.second.first, localMaximum.second.second);
433void ClusterFinderOriginal::buildPixArray()
440 std::pair<double, double> dim = mPreCluster->minPadDimensions(-1,
false);
441 double width[2] = {dim.first, dim.second};
444 double xy0[2] = {0., 0.};
445 bool found[2] = {
false,
false};
446 for (
const auto& pad : *mPreCluster) {
447 for (
int ixy = 0; ixy < 2; ++ixy) {
448 if (!found[ixy] && TMath::Abs(pad.dxy(ixy) -
width[ixy]) < SDistancePrecision) {
449 xy0[ixy] = pad.xy(ixy);
453 if (found[0] && found[1]) {
459 int plane0 = 0, plane1 = 1;
460 if (mPreCluster->multiplicity(0) == 0) {
462 }
else if (mPreCluster->multiplicity(1) == 0) {
467 double area[2][2] = {0.};
468 mPreCluster->area(plane0, area);
469 if (plane1 != plane0) {
470 double area2[2][2] = {0.};
471 mPreCluster->area(plane1, area2);
472 area[0][0] = TMath::Max(area[0][0], area2[0][0]);
473 area[0][1] = TMath::Min(area[0][1], area2[0][1]);
474 area[1][0] = TMath::Max(area[1][0], area2[1][0]);
475 area[1][1] = TMath::Min(area[1][1], area2[1][1]);
479 if (area[0][1] - area[0][0] < SDistancePrecision || area[1][1] - area[1][0] < SDistancePrecision) {
484 int nbins[2] = {0, 0};
485 for (
int ixy = 0; ixy < 2; ++ixy) {
487 double dist = (
area[ixy][0] - xy0[ixy]) /
width[ixy] / 2.;
489 nbins[ixy] = TMath::Ceil((area[ixy][1] - area[ixy][0]) /
width[ixy] / 2. -
precision);
494 TH2D hCharges(
"Charges",
"", nbins[0], area[0][0], area[0][1], nbins[1], area[1][0], area[1][1]);
495 TH2I hEntries(
"Entries",
"", nbins[0], area[0][0], area[0][1], nbins[1], area[1][0], area[1][1]);
496 for (
const auto& pad : *mPreCluster) {
497 ProjectPadOverPixels(pad, hCharges, hEntries);
501 for (
int i = 1;
i <= nbins[0]; ++
i) {
502 double x = hCharges.GetXaxis()->GetBinCenter(
i);
503 for (
int j = 1;
j <= nbins[1]; ++
j) {
504 int entries = hEntries.GetBinContent(
i,
j);
505 if (entries == 0 || (plane0 != plane1 && (entries < 1000 || entries % 1000 < 1))) {
508 double y = hCharges.GetYaxis()->GetBinCenter(
j);
509 double charge = hCharges.GetBinContent(
i,
j);
515 if (mPixels.size() == 1) {
516 auto& pixel = mPixels.front();
517 pixel.setdx(
width[0] / 2.);
518 pixel.setx(pixel.x() -
width[0] / 2.);
519 mPixels.emplace_back(pixel.x() +
width[0], pixel.y(),
width[0] / 2.,
width[1], pixel.charge());
523 size_t nPads = mPreCluster->multiplicity();
524 if (mPixels.size() > nPads) {
525 std::stable_sort(mPixels.begin(), mPixels.end(), [](
const PadOriginal& pixel1,
const PadOriginal& pixel2) {
526 return pixel1.charge() > pixel2.charge();
528 mPixels.erase(mPixels.begin() + nPads, mPixels.end());
533void ClusterFinderOriginal::ProjectPadOverPixels(
const PadOriginal& pad, TH2D& hCharges, TH2I& hEntries)
const
537 const TAxis* xaxis = hCharges.GetXaxis();
538 const TAxis* yaxis = hCharges.GetYaxis();
540 int iMin = TMath::Max(1, xaxis->FindBin(pad.x() - pad.dx() + SDistancePrecision));
541 int iMax = TMath::Min(hCharges.GetNbinsX(), xaxis->FindBin(pad.x() + pad.dx() - SDistancePrecision));
542 int jMin = TMath::Max(1, yaxis->FindBin(pad.y() - pad.dy() + SDistancePrecision));
543 int jMax = TMath::Min(hCharges.GetNbinsY(), yaxis->FindBin(pad.y() + pad.dy() - SDistancePrecision));
545 double charge = pad.charge();
546 int entry = 1 + pad.plane() * 999;
548 for (
int i = iMin;
i <= iMax; ++
i) {
549 for (
int j = jMin;
j <= jMax; ++
j) {
550 int entries = hEntries.GetBinContent(
i,
j);
551 hCharges.SetBinContent(
i,
j, (entries > 0) ? TMath::Min(hCharges.GetBinContent(
i,
j),
charge) :
charge);
552 hEntries.SetBinContent(
i,
j, entries +
entry);
558void ClusterFinderOriginal::findLocalMaxima(std::unique_ptr<TH2D>& histAnode,
559 std::multimap<
double, std::pair<int, int>, std::greater<>>& localMaxima)
566 double xMin(std::numeric_limits<double>::max()), xMax(-std::numeric_limits<double>::max());
567 double yMin(std::numeric_limits<double>::max()),
yMax(-std::numeric_limits<double>::max());
568 double dx(mPixels.front().dx()), dy(mPixels.front().dy());
569 for (
const auto& pixel : mPixels) {
570 xMin = TMath::Min(xMin, pixel.x());
571 xMax = TMath::Max(xMax, pixel.x());
572 yMin = TMath::Min(yMin, pixel.y());
575 int nBinsX = TMath::Nint((xMax - xMin) / dx / 2.) + 1;
576 int nBinsY = TMath::Nint((
yMax - yMin) / dy / 2.) + 1;
577 histAnode = std::make_unique<TH2D>(
"anode",
"anode", nBinsX, xMin - dx, xMax + dx, nBinsY, yMin - dy,
yMax + dy);
578 for (
const auto& pixel : mPixels) {
579 histAnode->Fill(pixel.x(), pixel.y(), pixel.charge());
583 std::vector<std::vector<int>> isLocalMax(nBinsX, std::vector<int>(nBinsY, 0));
584 for (
int j = 1;
j <= nBinsY; ++
j) {
585 for (
int i = 1;
i <= nBinsX; ++
i) {
586 if (isLocalMax[
i - 1][
j - 1] == 0 && histAnode->GetBinContent(
i,
j) >= mLowestPixelCharge) {
587 flagLocalMaxima(*histAnode,
i,
j, isLocalMax);
593 TAxis* xAxis = histAnode->GetXaxis();
594 TAxis* yAxis = histAnode->GetYaxis();
595 for (
int j = 1;
j <= nBinsY; ++
j) {
596 for (
int i = 1;
i <= nBinsX; ++
i) {
597 if (isLocalMax[
i - 1][
j - 1] > 0) {
598 localMaxima.emplace(histAnode->GetBinContent(
i,
j), std::make_pair(
i,
j));
599 auto itPixel =
findPad(mPixels, xAxis->GetBinCenter(
i), yAxis->GetBinCenter(
j), mLowestPixelCharge);
601 if (localMaxima.size() > 99) {
606 if (localMaxima.size() > 99) {
608 LOG(warning) <<
"Too many local maxima !!!";
615void ClusterFinderOriginal::flagLocalMaxima(
const TH2D& histAnode,
int i0,
int j0, std::vector<std::vector<int>>& isLocalMax)
const
622 int charge0 = TMath::Nint(histAnode.GetBinContent(i0, j0));
623 int iMin = TMath::Max(1, i0 - 1);
624 int iMax = TMath::Min(histAnode.GetNbinsX(), i0 + 1);
625 int jMin = TMath::Max(1, j0 - 1);
626 int jMax = TMath::Min(histAnode.GetNbinsY(), j0 + 1);
628 for (
int j = jMin;
j <= jMax; ++
j) {
630 for (
int i = iMin;
i <= iMax; ++
i) {
631 if (
i == i0 &&
j == j0) {
635 int charge = TMath::Nint(histAnode.GetBinContent(
i,
j));
637 isLocalMax[idxi0][idxj0] = -1;
639 }
else if (charge0 >
charge) {
640 isLocalMax[idxi][idxj] = -1;
641 }
else if (isLocalMax[idxi][idxj] == -1) {
642 isLocalMax[idxi0][idxj0] = -1;
644 }
else if (isLocalMax[idxi][idxj] == 0) {
645 isLocalMax[idxi0][idxj0] = 1;
646 flagLocalMaxima(histAnode,
i,
j, isLocalMax);
647 if (isLocalMax[idxi][idxj] == -1) {
648 isLocalMax[idxi0][idxj0] = -1;
651 isLocalMax[idxi][idxj] = -2;
656 isLocalMax[idxi0][idxj0] = 1;
660void ClusterFinderOriginal::restrictPreCluster(
const TH2D& histAnode,
int i0,
int j0)
667 const TAxis* xAxis = histAnode.GetXaxis();
668 const TAxis* yAxis = histAnode.GetYaxis();
669 double dx = xAxis->GetBinWidth(1) / 2.;
670 double dy = yAxis->GetBinWidth(1) / 2.;
671 double charge0 = histAnode.GetBinContent(i0, j0);
672 int iMin = TMath::Max(1, i0 - 1);
673 int iMax = TMath::Min(histAnode.GetNbinsX(), i0 + 1);
674 int jMin = TMath::Max(1, j0 - 1);
675 int jMax = TMath::Min(histAnode.GetNbinsY(), j0 + 1);
676 for (
int j = jMin;
j <= jMax; ++
j) {
677 for (
int i = iMin;
i <= iMax; ++
i) {
678 double charge = histAnode.GetBinContent(
i,
j);
679 if (
charge >= mLowestPixelCharge &&
charge <= charge0) {
680 mPixels.emplace_back(xAxis->GetBinCenter(
i), yAxis->GetBinCenter(
j), dx, dy,
charge);
686 for (
auto& pad : *mPreCluster) {
688 for (
const auto& pixel : mPixels) {
698void ClusterFinderOriginal::processSimple()
708 std::vector<double> coef(0);
709 std::vector<double> prob(0);
710 computeCoefficients(coef, prob);
713 for (
int ipix = 0; ipix < mPixels.size(); ++ipix) {
714 if (prob[ipix] < 0.01) {
715 mPixels[ipix].setCharge(0);
720 double qTot = mlem(coef, prob, 15);
723 if (qTot < 1.e-4 || (mPreCluster->multiplicity() < 3 && qTot < mLowestClusterCharge)) {
728 for (
auto& pad : *mPreCluster) {
729 if (!pad.isSaturated()) {
735 std::vector<int>
pixels(mPixels.size());
739 double fitRange[2][2] = {{std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()},
740 {std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()}};
741 for (
const auto& pixel : mPixels) {
742 fitRange[0][0] = TMath::Min(fitRange[0][0], pixel.x() - 3. * pixel.dx());
743 fitRange[0][1] = TMath::Max(fitRange[0][1], pixel.x() + 3. * pixel.dx());
744 fitRange[1][0] = TMath::Min(fitRange[1][0], pixel.y() - 3. * pixel.dy());
745 fitRange[1][1] = TMath::Max(fitRange[1][1], pixel.y() + 3. * pixel.dy());
749 double fitParam[SNFitParamMax + 1] = {0.};
750 fit({&
pixels}, fitRange, fitParam);
754void ClusterFinderOriginal::process()
766 for (
const auto& pad : *mPreCluster) {
773 double xMin(std::numeric_limits<double>::max()), xMax(-std::numeric_limits<double>::max());
774 double yMin(std::numeric_limits<double>::max()),
yMax(-std::numeric_limits<double>::max());
775 for (
const auto& pixel : mPixels) {
776 xMin = TMath::Min(xMin, pixel.x());
777 xMax = TMath::Max(xMax, pixel.x());
778 yMin = TMath::Min(yMin, pixel.y());
782 std::vector<double> coef(0);
783 std::vector<double> prob(0);
784 std::unique_ptr<TH2D> histMLEM(
nullptr);
788 computeCoefficients(coef, prob);
791 for (
int ipix = 0; ipix < mPixels.size(); ++ipix) {
792 if (prob[ipix] < 0.01) {
793 mPixels[ipix].setCharge(0);
798 double qTot = mlem(coef, prob, 15);
801 if (qTot < 1.e-4 || (npadOK < 3 && qTot < mLowestClusterCharge)) {
806 double dx(mPixels.front().dx()), dy(mPixels.front().dy());
807 int nBinsX = TMath::Nint((xMax - xMin) / dx / 2.) + 1;
808 int nBinsY = TMath::Nint((
yMax - yMin) / dy / 2.) + 1;
809 histMLEM.reset(
nullptr);
810 histMLEM = std::make_unique<TH2D>(
"mlem",
"mlem", nBinsX, xMin - dx, xMax + dx, nBinsY, yMin - dy,
yMax + dy);
811 for (
const auto& pixel : mPixels) {
812 histMLEM->Fill(pixel.x(), pixel.y(), pixel.charge());
816 if ((dx < 0.07 || dy < 0.07) && dy < dx) {
821 double xyCOG[2] = {0., 0.};
822 findCOG(*histMLEM, xyCOG);
825 refinePixelArray(xyCOG, npadOK, xMin, xMax, yMin,
yMax);
829 double threshold = TMath::Min(TMath::Max(histMLEM->GetMaximum() / 100., 2.0 * mLowestPixelCharge), 100.0 * mLowestPixelCharge);
830 cleanPixelArray(threshold, prob);
833 double qTot = mlem(coef, prob, 2);
836 if (qTot < 2. * mLowestPixelCharge) {
841 for (
const auto& pixel : mPixels) {
842 histMLEM->SetBinContent(histMLEM->GetXaxis()->FindBin(pixel.x()), histMLEM->GetYaxis()->FindBin(pixel.y()), pixel.charge());
846 split(*histMLEM, coef);
850void ClusterFinderOriginal::addVirtualPad()
855 const mapping::CathodeSegmentation* cathSeg[2] = {&mSegmentation->
bending(), &mSegmentation->
nonBending()};
858 int iPlaneXY[2] = {1, 0};
862 std::pair<double, double> dim0 = mPreCluster->minPadDimensions(0,
PadOriginal::kZero,
true);
863 std::pair<double, double> dim1 = mPreCluster->minPadDimensions(1,
PadOriginal::kZero,
true);
864 bool sameSizeY = TMath::Abs(dim0.second - dim1.second) < SDistancePrecision;
865 bool addVirtualPad[2] = {
false,
false};
866 std::pair<int, int> nPadsXY0 = mPreCluster->sizeInPads(iPlaneXY[0],
PadOriginal::kZero);
867 std::pair<int, int> nPadsXY1 = mPreCluster->sizeInPads(iPlaneXY[1],
PadOriginal::kZero);
868 if (nPadsXY0.first == 2 && (!sameSizeY || nPadsXY1.first <= 2)) {
869 addVirtualPad[0] =
true;
871 if (nPadsXY1.second == 2 && (!sameSizeY || nPadsXY0.second <= 2)) {
872 addVirtualPad[1] =
true;
875 double chargeReduction[2] = {100., 15.};
876 for (
int ixy = 0; ixy < 2; ++ixy) {
879 if (!addVirtualPad[ixy]) {
885 long iPadMax[2] = {-1, -1};
886 double chargeMax[2] = {0., 0.};
887 double limits[2] = {std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()};
888 for (
int i = 0;
i < mPreCluster->multiplicity(); ++
i) {
889 const auto& pad = mPreCluster->pad(
i);
890 if (pad.plane() != iPlaneXY[ixy]) {
893 if (pad.charge() > chargeMax[0]) {
894 chargeMax[1] = chargeMax[0];
895 chargeMax[0] = pad.charge();
896 iPadMax[1] = iPadMax[0];
898 }
else if (pad.charge() > chargeMax[1]) {
899 chargeMax[1] = pad.charge();
902 double xy = pad.xy(ixy);
903 if (xy < limits[0]) {
906 if (xy > limits[1]) {
913 int n = (chargeMax[1] / chargeMax[0] < 0.5) ? 1 : 2;
915 for (
int i = 0;
i <
n; ++
i) {
917 if (iPadMax[
i] < 0) {
918 throw std::runtime_error(
"This plane should contain at least 2 pads!?");
922 const auto& pad = mPreCluster->pad(iPadMax[
i]);
924 double xy = pad.xy(ixy);
925 if (TMath::Abs(xy - limits[0]) < SDistancePrecision) {
927 }
else if (TMath::Abs(xy - limits[1]) < SDistancePrecision) {
934 if (
side == sideDone) {
939 double pos[2] = {pad.x(), pad.y()};
940 pos[ixy] +=
side * (pad.dxy(ixy) + SDistancePrecision);
941 pos[1 - ixy] += SDistancePrecision;
942 int cathPadIdx = cathSeg[iPlaneXY[ixy]]->findPadByPosition(
pos[0],
pos[1]);
943 if (!cathSeg[iPlaneXY[ixy]]->
isValid(cathPadIdx)) {
948 double charge = TMath::Max(TMath::Min(chargeMax[
i] / chargeReduction[ixy], mLowestPadCharge), 2. * mLowestPixelCharge);
949 mPreCluster->addPad(cathSeg[iPlaneXY[ixy]]->padPositionX(cathPadIdx), cathSeg[iPlaneXY[ixy]]->padPositionY(cathPadIdx),
950 cathSeg[iPlaneXY[ixy]]->
padSizeX(cathPadIdx) / 2., cathSeg[iPlaneXY[ixy]]->
padSizeY(cathPadIdx) / 2.,
951 charge,
false, iPlaneXY[ixy], -1, pad.status());
959void ClusterFinderOriginal::computeCoefficients(std::vector<double>& coef, std::vector<double>& prob)
const
963 coef.assign(mPreCluster->multiplicity() * mPixels.size(), 0.);
964 prob.assign(mPixels.size(), 0.);
967 for (
const auto& pad : *mPreCluster) {
971 iCoef += mPixels.size();
975 for (
int i = 0;
i < mPixels.size(); ++
i) {
978 coef[iCoef] = chargeIntegration(mPixels[
i].
x(), mPixels[
i].
y(), pad);
981 prob[
i] += coef[iCoef];
989double ClusterFinderOriginal::mlem(
const std::vector<double>& coef,
const std::vector<double>& prob,
int nIter)
995 double maxProb = *std::max_element(prob.begin(), prob.end());
996 std::vector<double> padSum(mPreCluster->multiplicity(), 0.);
998 for (
int iter = 0; iter < nIter; ++iter) {
1002 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
1003 const auto& pad = mPreCluster->pad(iPad);
1005 iCoef += mPixels.size();
1009 for (
const auto& pixel : mPixels) {
1010 padSum[iPad] += pixel.charge() * coef[iCoef++];
1015 for (
int iPix = 0; iPix < mPixels.size(); ++iPix) {
1018 if (prob[iPix] < 0.01) {
1019 mPixels[iPix].setCharge(0.);
1023 double pixelSum(0.);
1024 double pixelNorm(maxProb);
1025 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
1028 const auto& pad = mPreCluster->pad(iPad);
1034 int iCoef = iPad * mPixels.size() + iPix;
1035 if (pad.isSaturated() && padSum[iPad] > pad.charge()) {
1036 pixelNorm -= coef[iCoef];
1040 if (padSum[iPad] > 1.e-6) {
1041 pixelSum += pad.charge() * coef[iCoef] / padSum[iPad];
1046 if (pixelNorm > 1.e-6) {
1047 mPixels[iPix].setCharge(mPixels[iPix].
charge() * pixelSum / pixelNorm);
1048 qTot += mPixels[iPix].charge();
1050 mPixels[iPix].setCharge(0.);
1064void ClusterFinderOriginal::findCOG(
const TH2D& histMLEM,
double xy[2])
const
1069 int ix0(0), iy0(0), iz0(0);
1070 double chargeThreshold = histMLEM.GetBinContent(histMLEM.GetMaximumBin(ix0, iy0, iz0)) / 10.;
1071 int ixMin = TMath::Max(1, ix0 - 1);
1072 int ixMax = TMath::Min(histMLEM.GetNbinsX(), ix0 + 1);
1073 int iyMin = TMath::Max(1, iy0 - 1);
1074 int iyMax = TMath::Min(histMLEM.GetNbinsY(), iy0 + 1);
1077 const TAxis* xAxis = histMLEM.GetXaxis();
1078 const TAxis* yAxis = histMLEM.GetYaxis();
1079 double xq(0.), yq(0.), q(0.);
1080 bool onePixelWidthX(
true), onePixelWidthY(
true);
1081 for (
int iy = iyMin;
iy <= iyMax; ++
iy) {
1082 for (
int ix = ixMin;
ix <= ixMax; ++
ix) {
1083 double charge = histMLEM.GetBinContent(ix, iy);
1084 if (
charge >= chargeThreshold) {
1085 xq += xAxis->GetBinCenter(ix) *
charge;
1086 yq += yAxis->GetBinCenter(iy) *
charge;
1089 onePixelWidthX =
false;
1092 onePixelWidthY =
false;
1099 if (onePixelWidthY) {
1100 double xPixel(0.), yPixel(0.), chargePixel(0.);
1102 for (
int iy = iyMin;
iy <= iyMax; ++
iy) {
1104 for (
int ix = ixMin;
ix <= ixMax; ++
ix) {
1105 double charge = histMLEM.GetBinContent(ix, iy);
1106 if (
charge > chargePixel) {
1107 xPixel = xAxis->GetBinCenter(ix);
1108 yPixel = yAxis->GetBinCenter(iy);
1115 xq += xPixel * chargePixel;
1116 yq += yPixel * chargePixel;
1118 if (ixPixel != ix0) {
1119 onePixelWidthX =
false;
1124 if (onePixelWidthX) {
1125 double xPixel(0.), yPixel(0.), chargePixel(0.);
1126 for (
int ix = ixMin;
ix <= ixMax; ++
ix) {
1128 for (
int iy = iyMin;
iy <= iyMax; ++
iy) {
1129 double charge = histMLEM.GetBinContent(ix, iy);
1130 if (
charge > chargePixel) {
1131 xPixel = xAxis->GetBinCenter(ix);
1132 yPixel = yAxis->GetBinCenter(iy);
1138 xq += xPixel * chargePixel;
1139 yq += yPixel * chargePixel;
1149void ClusterFinderOriginal::refinePixelArray(
const double xyCOG[2],
size_t nPixMax,
double& xMin,
double& xMax,
double& yMin,
double&
yMax)
1157 xMin = std::numeric_limits<double>::max();
1158 xMax = -std::numeric_limits<double>::max();
1159 yMin = std::numeric_limits<double>::max();
1160 yMax = -std::numeric_limits<double>::max();
1163 std::stable_sort(mPixels.begin(), mPixels.end(), [](
const PadOriginal& pixel1,
const PadOriginal& pixel2) {
1164 return (pixel1.status() == PadOriginal::kMustKeep && pixel2.status() != PadOriginal::kMustKeep) ||
1165 (pixel1.status() == pixel2.status() && pixel1.charge() > pixel2.charge());
1167 double pixMinCharge = TMath::Min(0.01 * mPixels.front().charge(), 100. * mLowestPixelCharge);
1170 double width[2] = {mPixels.front().dx(), mPixels.front().dy()};
1171 double shift[2] = {0., 0.};
1174 shift[0] = -
width[0];
1177 shift[1] = -
width[1];
1181 double shiftToCOG[2] = {0., 0.};
1182 for (
int ixy = 0; ixy < 2; ++ixy) {
1183 shiftToCOG[ixy] = mPixels.front().xy(ixy) + shift[ixy] - xyCOG[ixy];
1184 shiftToCOG[ixy] -=
int(shiftToCOG[ixy] /
width[ixy] / 2.) *
width[ixy] * 2.;
1193 auto& pixel = mPixels[
i];
1194 if (nNewPixels == nPixMax || (pixel.charge() < pixMinCharge && pixel.status() !=
PadOriginal::kMustKeep)) {
1195 mPixels.erase(mPixels.begin() +
i, mPixels.begin() + nPixels);
1200 pixel.setx(pixel.x() + shift[0] - shiftToCOG[0]);
1201 pixel.sety(pixel.y() + shift[1] - shiftToCOG[1]);
1202 pixel.setdx(
width[0]);
1203 pixel.setdy(
width[1]);
1204 pixel.setCharge(mLowestPadCharge);
1205 xMin = TMath::Min(xMin, pixel.x());
1206 yMin = TMath::Min(yMin, pixel.y());
1210 if (nNewPixels == nPixMax) {
1211 xMax = TMath::Max(xMax, pixel.x());
1212 yMax = TMath::Max(
yMax, pixel.y());
1217 mPixels.emplace_back(pixel);
1218 auto& pixel2 = mPixels.back();
1219 pixel2.setx(pixel2.x() - 2. * shift[0]);
1220 pixel2.sety(pixel2.y() - 2. * shift[1]);
1222 xMax = TMath::Max(xMax, pixel2.x());
1223 yMax = TMath::Max(
yMax, pixel2.y());
1228 if (mPixels.size() < nPixMax && xyCOG[1] +
width[1] >
yMax) {
1230 mPixels.emplace_back(mPixels.front());
1231 mPixels.back().setx(xyCOG[0]);
1232 mPixels.back().sety(
yMax);
1234 if (mPixels.size() < nPixMax && xyCOG[1] -
width[1] < yMin) {
1235 yMin = xyCOG[1] - 2. *
width[1];
1236 mPixels.emplace_back(mPixels.front());
1237 mPixels.back().setx(xyCOG[0]);
1238 mPixels.back().sety(yMin);
1240 if (mPixels.size() < nPixMax && xyCOG[0] +
width[0] > xMax) {
1241 xMax = xyCOG[0] + 2. *
width[0];
1242 mPixels.emplace_back(mPixels.front());
1243 mPixels.back().setx(xMax);
1244 mPixels.back().sety(xyCOG[1]);
1246 if (mPixels.size() < nPixMax && xyCOG[0] -
width[0] < xMin) {
1247 xMin = xyCOG[0] - 2. *
width[0];
1248 mPixels.emplace_back(mPixels.front());
1249 mPixels.back().setx(xMin);
1250 mPixels.back().sety(xyCOG[1]);
1255void ClusterFinderOriginal::cleanPixelArray(
double threshold, std::vector<double>& prob)
1261 for (
int i = 0;
i < mPixels.size(); ++
i) {
1263 auto& pixel = mPixels[
i];
1264 if (pixel.charge() >= threshold) {
1272 if (pixel.charge() <= 0.) {
1278 double distMin(std::numeric_limits<double>::max());
1279 for (
int j = 0;
j < mPixels.size(); ++
j) {
1280 auto& pixel2 = mPixels[
j];
1281 if (
j !=
i && pixel2.charge() >= threshold) {
1282 double distX = (pixel.x() - pixel2.x()) / pixel2.dx();
1283 double distY = (pixel.y() - pixel2.y()) / pixel2.dy();
1285 if (dist < distMin) {
1291 if (iNeighbour < 0) {
1292 LOG(info) <<
"There is no pixel above the threshold!?";
1297 mPixels[iNeighbour].setCharge(mPixels[iNeighbour].
charge() + pixel.charge());
1298 pixel.setCharge(0.);
1303int ClusterFinderOriginal::fit(
const std::vector<
const std::vector<int>*>& clustersOfPixels,
1304 const double fitRange[2][2],
double fitParam[SNFitParamMax + 1])
1316 if (clustersOfPixels.empty() || clustersOfPixels.size() > SNFitClustersMax) {
1317 throw std::runtime_error(std::string(
"Cannot fit ") + clustersOfPixels.size() +
" clusters at a time");
1321 int nRealPadsToFit(0), nVirtualPadsToFit(0);
1322 double averagePadCharge(0.);
1323 for (
const auto& pad : *mPreCluster) {
1325 averagePadCharge += pad.charge();
1329 ++nVirtualPadsToFit;
1334 if (nRealPadsToFit < 2) {
1335 fitParam[SNFitParamMax] = 0.;
1338 averagePadCharge /= nRealPadsToFit;
1342 double xMean(0.), yMean(0.);
1343 fitParam[SNFitParamMax] = 0.;
1344 std::multimap<double, std::pair<double, double>, std::greater<>> xySeed{};
1345 for (
const auto iPixels : clustersOfPixels) {
1346 double chargeMax(0.), xSeed(0.), ySeed(0.);
1347 for (
auto iPixel : *iPixels) {
1348 const auto& pixel = mPixels[iPixel];
1349 double charge = pixel.charge();
1350 fitParam[SNFitParamMax] +=
charge;
1351 xMean += pixel.x() *
charge;
1352 yMean += pixel.y() *
charge;
1353 if (
charge > chargeMax) {
1359 xySeed.emplace(chargeMax, std::make_pair(xSeed, ySeed));
1361 xMean /= fitParam[SNFitParamMax];
1362 yMean /= fitParam[SNFitParamMax];
1366 if (xySeed.size() > 1) {
1367 int max = TMath::Min(SNFitClustersMax, (nRealPadsToFit + 1) / 3);
1369 if ((nPadsXY.first < 3 && nPadsXY.second < 3) ||
1370 (nPadsXY.first == 3 && nPadsXY.second < 3) ||
1371 (nPadsXY.first < 3 && nPadsXY.second == 3)) {
1375 if (xySeed.size() >
max) {
1376 xySeed.erase(std::next(xySeed.begin(),
max), xySeed.end());
1382 double param[SNFitParamMax + 2] = {xMean, yMean, 0.6, xMean, yMean, 0.6, xMean, yMean, fitParam[SNFitParamMax], averagePadCharge};
1383 double parmin[SNFitParamMax] = {fitRange[0][0], fitRange[1][0], 1.e-9, fitRange[0][0], fitRange[1][0], 1.e-9, fitRange[0][0], fitRange[1][0]};
1384 double parmax[SNFitParamMax] = {fitRange[0][1], fitRange[1][1], 1., fitRange[0][1], fitRange[1][1], 1., fitRange[0][1], fitRange[1][1]};
1385 if (xySeed.size() > 1) {
1387 for (
const auto& seed : xySeed) {
1388 param[iParam++] = seed.second.first;
1389 param[iParam++] = seed.second.second;
1396 double chi2n0(std::numeric_limits<float>::max());
1399 for (
int nFitClusters = 1; nFitClusters <= xySeed.size(); ++nFitClusters) {
1402 nParamUsed = 3 * nFitClusters - 1;
1405 double chi2 = fit(
param, parmin, parmax, nParamUsed, nTrials);
1408 int dof = TMath::Max(nRealPadsToFit + nVirtualPadsToFit - nParamUsed, 1);
1409 double chi2n = chi2 / dof;
1410 if (nParamUsed > 2 &&
1411 (chi2n > chi2n0 || (nFitClusters == xySeed.size() && chi2n * (1 + TMath::Min(1 -
param[nParamUsed - 3], 0.25)) > chi2n0))) {
1418 if (nPadsXY.first == 1) {
1419 for (
int i = 0;
i < nParamUsed; ++
i) {
1420 if (
i == 0 ||
i == 3 ||
i == 6) {
1425 if (nPadsXY.second == 1) {
1426 for (
int i = 0;
i < nParamUsed; ++
i) {
1427 if (
i == 1 ||
i == 4 ||
i == 7) {
1434 for (
int i = 0;
i < nParamUsed; ++
i) {
1447 double chargeFraction[SNFitClustersMax] = {0.};
1448 param2ChargeFraction(fitParam, nParamUsed, chargeFraction);
1449 for (
int iParam = 0; iParam < nParamUsed; iParam += 3) {
1450 if (chargeFraction[iParam / 3] * fitParam[SNFitParamMax] >= mLowestClusterCharge) {
1451 mClusters.push_back({
static_cast<float>(fitParam[iParam]),
static_cast<float>(fitParam[iParam + 1]), 0., 0., 0., 0, 0, 0});
1459double ClusterFinderOriginal::fit(
double currentParam[SNFitParamMax + 2],
1460 const double parmin[SNFitParamMax],
const double parmax[SNFitParamMax],
1461 int nParamUsed,
int& nTrials)
const
1467 static const double defaultShift[SNFitParamMax] = {0.01, 0.002, 0.02, 0.01, 0.002, 0.02, 0.01, 0.002};
1469 double shift[SNFitParamMax] = {0.};
1470 for (
int i = 0;
i < nParamUsed; ++
i) {
1471 shift[
i] = defaultShift[
i];
1475 double param[2][SNFitParamMax] = {{0.}, {0.}};
1476 double deriv[2][SNFitParamMax] = {{0.}, {0.}};
1477 double chi2[2] = {0., std::numeric_limits<float>::max()};
1480 double shiftSave(0.);
1481 int nSimilarSteps[SNFitParamMax] = {0};
1482 int nLoop(0), nFail(0);
1488 int iCurrentParam = 1 - iBestParam;
1491 chi2[iCurrentParam] = computeChi2(currentParam, nParamUsed);
1495 double deriv2nd[SNFitParamMax] = {0.};
1496 for (
int i = 0;
i < nParamUsed; ++
i) {
1497 param[iCurrentParam][
i] = currentParam[
i];
1498 currentParam[
i] += defaultShift[
i] / 10.;
1499 double chi2Shift = computeChi2(currentParam, nParamUsed);
1501 deriv[iCurrentParam][
i] = (chi2Shift - chi2[iCurrentParam]) / defaultShift[
i] * 10;
1503 currentParam[
i] -= defaultShift[
i] / 10.;
1507 if (nTrials > 2000) {
1512 iBestParam = chi2[0] < chi2[1] ? 0 : 1;
1513 nFail = iBestParam == iCurrentParam ? 0 : nFail + 1;
1520 double stepMax(0.), derivMax(0.);
1522 for (
int i = 0;
i < nParamUsed; ++
i) {
1525 double previousShift = shift[
i];
1528 shift[
i] = TMath::Sign(defaultShift[
i], -deriv[iCurrentParam][
i]);
1529 }
else if (TMath::Abs(deriv[0][
i]) < 1.e-3 && TMath::Abs(deriv[1][
i]) < 1.e-3) {
1532 }
else if ((deriv[iBestParam][
i] * deriv[1 - iBestParam][
i] > 0. &&
1533 TMath::Abs(deriv[iBestParam][
i]) > TMath::Abs(deriv[1 - iBestParam][
i])) ||
1534 TMath::Abs(deriv[0][
i] - deriv[1][
i]) < 1.e-3 ||
1535 TMath::Abs(deriv2nd[
i]) < 1.e-6) {
1537 shift[
i] = -TMath::Sign(shift[
i], (chi2[0] - chi2[1]) * (
param[0][
i] -
param[1][
i]));
1539 if (iBestParam == iCurrentParam) {
1540 if (nSimilarSteps[
i] > 1) {
1547 shift[
i] = deriv2nd[
i] != 0. ? -deriv[iBestParam][
i] / deriv2nd[
i] : 0.;
1548 nSimilarSteps[
i] = 0;
1552 stepMax = TMath::Max(stepMax, TMath::Abs(shift[
i]) / defaultShift[
i]);
1553 if (TMath::Abs(deriv[iBestParam][
i]) > derivMax) {
1555 derivMax = TMath::Abs(deriv[iBestParam][
i]);
1559 if (TMath::Abs(shift[
i]) / defaultShift[
i] > 10.) {
1560 shift[
i] = TMath::Sign(10., shift[
i]) * defaultShift[
i];
1564 if (iBestParam != iCurrentParam) {
1565 nSimilarSteps[
i] = 0;
1566 currentParam[
i] =
param[iBestParam][
i];
1567 if (TMath::Abs(shift[
i] + previousShift) > 0.1 * defaultShift[
i]) {
1568 shift[
i] = (shift[
i] + previousShift) / 2.;
1575 if (TMath::Abs(shift[
i] * deriv[iBestParam][
i]) > chi2[iBestParam]) {
1576 shift[
i] = TMath::Sign(chi2[iBestParam] / deriv[iBestParam][
i], shift[
i]);
1580 if (nSimilarSteps[
i] < 3) {
1581 double scMax = 1. + 4. / TMath::Max(nLoop / 2., 1.);
1582 if (TMath::Abs(previousShift) > 0. && TMath::Abs(shift[
i] / previousShift) > scMax) {
1583 shift[
i] = TMath::Sign(previousShift * scMax, shift[
i]);
1588 currentParam[
i] += shift[
i];
1589 if (currentParam[
i] < parmin[
i]) {
1590 shift[
i] = parmin[
i] - currentParam[
i];
1591 currentParam[
i] = parmin[
i];
1592 }
else if (currentParam[
i] > parmax[
i]) {
1593 shift[
i] = parmax[
i] - currentParam[
i];
1594 currentParam[
i] = parmax[
i];
1599 if (stepMax < 1. && derivMax < 2.) {
1604 if (shift[iDerivMax] == 0.) {
1605 shift[iDerivMax] = defaultShift[iDerivMax] / 10.;
1606 currentParam[iDerivMax] += shift[iDerivMax];
1611 if (nSimilarSteps[iDerivMax] == 0 && derivMax > 0.5 && nLoop > 9) {
1612 if (deriv2nd[iDerivMax] != 0. && TMath::Abs(deriv[iBestParam][iDerivMax] / deriv2nd[iDerivMax] / shift[iDerivMax]) > 10.) {
1613 if (iBestParam == iCurrentParam) {
1614 deriv2nd[iDerivMax] = -deriv2nd[iDerivMax];
1616 shift[iDerivMax] = -deriv[iBestParam][iDerivMax] / deriv2nd[iDerivMax] / 10.;
1617 currentParam[iDerivMax] += shift[iDerivMax];
1618 if (iBestParam == iCurrentParam) {
1619 shiftSave = shift[iDerivMax];
1623 currentParam[iDerivMax] -= shift[iDerivMax];
1624 shift[iDerivMax] = 4. * shiftSave * (gRandom->Rndm(0) - 0.5);
1625 currentParam[iDerivMax] += shift[iDerivMax];
1631 for (
int i = 0;
i < nParamUsed; ++
i) {
1632 currentParam[
i] =
param[iBestParam][
i];
1634 return chi2[iBestParam];
1638double ClusterFinderOriginal::computeChi2(
const double param[SNFitParamMax + 2],
int nParamUsed)
const
1647 double chargeFraction[SNFitClustersMax] = {0.};
1648 param2ChargeFraction(
param, nParamUsed, chargeFraction);
1651 for (
const auto& pad : *mPreCluster) {
1659 double padChargeFit(0.);
1660 for (
int iParam = 0; iParam < nParamUsed; iParam += 3) {
1661 padChargeFit += chargeIntegration(
param[iParam],
param[iParam + 1], pad) * chargeFraction[iParam / 3];
1663 padChargeFit *=
param[SNFitParamMax];
1666 double delta = padChargeFit - pad.charge();
1667 chi2 += delta * delta / pad.charge();
1670 return chi2 /
param[SNFitParamMax + 1];
1674void ClusterFinderOriginal::param2ChargeFraction(
const double param[SNFitParamMax],
int nParamUsed,
1675 double fraction[SNFitClustersMax])
const
1678 if (nParamUsed == 2) {
1680 }
else if (nParamUsed == 5) {
1681 fraction[0] =
param[2];
1682 fraction[1] = TMath::Max(1. - fraction[0], 0.);
1684 fraction[0] =
param[2];
1685 fraction[1] = TMath::Max((1. - fraction[0]) *
param[5], 0.);
1686 fraction[2] = TMath::Max(1. - fraction[0] - fraction[1], 0.);
1691float ClusterFinderOriginal::chargeIntegration(
double x,
double y,
const PadOriginal& pad)
const
1694 double xPad = pad.x() -
x;
1695 double yPad = pad.y() -
y;
1696 return mMathieson->
integrate(xPad - pad.dx(), yPad - pad.dy(), xPad + pad.dx(), yPad + pad.dy());
1700void ClusterFinderOriginal::split(
const TH2D& histMLEM,
const std::vector<double>& coef)
1707 std::vector<double> padCharges(0);
1708 padCharges.reserve(mPreCluster->multiplicity());
1709 for (
const auto& pad : *mPreCluster) {
1710 padCharges.push_back(pad.charge());
1714 int nBinsX = histMLEM.GetNbinsX();
1715 int nBinsY = histMLEM.GetNbinsY();
1716 std::vector<std::vector<int>> clustersOfPixels{};
1717 std::vector<std::vector<bool>> isUsed(nBinsX, std::vector<bool>(nBinsY,
false));
1718 for (
int j = 1;
j <= nBinsY; ++
j) {
1719 for (
int i = 1;
i <= nBinsX; ++
i) {
1720 if (!isUsed[
i - 1][
j - 1] && histMLEM.GetBinContent(
i,
j) >= mLowestPixelCharge) {
1722 clustersOfPixels.emplace_back();
1723 addPixel(histMLEM,
i,
j, clustersOfPixels.back(), isUsed);
1727 if (clustersOfPixels.size() > 200) {
1728 throw std::runtime_error(
"Too many clusters of pixels!");
1732 std::vector<std::vector<double>> couplingClPad(clustersOfPixels.size(), std::vector<double>(mPreCluster->multiplicity(), 0.));
1733 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
1734 int idx0 = iPad * mPixels.size();
1735 for (
int iCluster = 0; iCluster < clustersOfPixels.size(); ++iCluster) {
1736 for (
auto ipixel : clustersOfPixels[iCluster]) {
1737 if (coef[idx0 + ipixel] >= SLowestCoupling) {
1738 couplingClPad[iCluster][iPad] += coef[idx0 + ipixel];
1745 std::vector<std::vector<double>> couplingClCl(clustersOfPixels.size(), std::vector<double>(clustersOfPixels.size(), 0.));
1746 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
1747 if (!mPreCluster->pad(iPad).isSaturated()) {
1748 for (
int iCluster1 = 0; iCluster1 < clustersOfPixels.size(); ++iCluster1) {
1749 if (couplingClPad[iCluster1][iPad] >= SLowestCoupling) {
1750 for (
int iCluster2 = iCluster1 + 1; iCluster2 < clustersOfPixels.size(); ++iCluster2) {
1751 if (couplingClPad[iCluster2][iPad] >= SLowestCoupling) {
1752 couplingClCl[iCluster1][iCluster2] += TMath::Sqrt(couplingClPad[iCluster1][iPad] * couplingClPad[iCluster2][iPad]);
1759 for (
int iCluster1 = 0; iCluster1 < clustersOfPixels.size(); ++iCluster1) {
1760 for (
int iCluster2 = iCluster1 + 1; iCluster2 < clustersOfPixels.size(); ++iCluster2) {
1761 couplingClCl[iCluster2][iCluster1] = couplingClCl[iCluster1][iCluster2];
1766 const TAxis* xAxis = histMLEM.GetXaxis();
1767 const TAxis* yAxis = histMLEM.GetYaxis();
1768 double fitRange[2][2] = {{xAxis->GetXmin() - xAxis->GetBinWidth(1), xAxis->GetXmax() + xAxis->GetBinWidth(1)},
1769 {yAxis->GetXmin() - yAxis->GetBinWidth(1), yAxis->GetXmax() + yAxis->GetBinWidth(1)}};
1771 std::vector<bool> isClUsed(clustersOfPixels.size(),
false);
1772 std::vector<int> coupledClusters{};
1773 std::vector<int> clustersForFit{};
1774 std::vector<const std::vector<int>*> clustersOfPixelsForFit{};
1775 for (
int iCluster = 0; iCluster < clustersOfPixels.size(); ++iCluster) {
1778 if (isClUsed[iCluster]) {
1783 coupledClusters.clear();
1784 addCluster(iCluster, coupledClusters, isClUsed, couplingClCl);
1786 while (coupledClusters.size() > 0) {
1790 clustersForFit.clear();
1791 if (coupledClusters.size() <= SNFitClustersMax) {
1792 clustersForFit.swap(coupledClusters);
1794 extractLeastCoupledClusters(coupledClusters, clustersForFit, couplingClCl);
1798 int nSelectedPads = selectPads(coupledClusters, clustersForFit, couplingClPad);
1802 if (nSelectedPads < 3 && coupledClusters.size() + clustersForFit.size() > 1) {
1803 for (
auto& pad : *mPreCluster) {
1808 if (coupledClusters.size() > 0) {
1809 merge(clustersForFit, coupledClusters, clustersOfPixels, couplingClCl, couplingClPad);
1815 clustersOfPixelsForFit.clear();
1816 for (
auto iCluster : clustersForFit) {
1817 clustersOfPixelsForFit.push_back(&clustersOfPixels[iCluster]);
1819 double fitParam[SNFitParamMax + 1] = {0.};
1820 int nParamUsed = fit(clustersOfPixelsForFit, fitRange, fitParam);
1823 updatePads(fitParam, nParamUsed);
1829 for (
auto& pad : *mPreCluster) {
1830 pad.setCharge(padCharges[iPad++]);
1835void ClusterFinderOriginal::addPixel(
const TH2D& histMLEM,
int i0,
int j0, std::vector<int>&
pixels, std::vector<std::vector<bool>>& isUsed)
1840 auto itPixel =
findPad(mPixels, histMLEM.GetXaxis()->GetBinCenter(i0), histMLEM.GetYaxis()->GetBinCenter(j0), mLowestPixelCharge);
1841 pixels.push_back(std::distance(mPixels.begin(), itPixel));
1842 isUsed[i0 - 1][j0 - 1] =
true;
1844 int iMin = TMath::Max(1, i0 - 1);
1845 int iMax = TMath::Min(histMLEM.GetNbinsX(), i0 + 1);
1846 int jMin = TMath::Max(1, j0 - 1);
1847 int jMax = TMath::Min(histMLEM.GetNbinsY(), j0 + 1);
1848 for (
int j = jMin;
j <= jMax; ++
j) {
1849 for (
int i = iMin;
i <= iMax; ++
i) {
1850 if (!isUsed[
i - 1][
j - 1] && (
i == i0 ||
j == j0) && histMLEM.GetBinContent(
i,
j) >= mLowestPixelCharge) {
1851 addPixel(histMLEM,
i,
j,
pixels, isUsed);
1858void ClusterFinderOriginal::addCluster(
int iCluster, std::vector<int>& coupledClusters, std::vector<bool>& isClUsed,
1859 const std::vector<std::vector<double>>& couplingClCl)
const
1864 coupledClusters.push_back(iCluster);
1865 isClUsed[iCluster] =
true;
1867 for (
int iCluster2 = 0; iCluster2 < couplingClCl.size(); ++iCluster2) {
1868 if (!isClUsed[iCluster2] && couplingClCl[iCluster][iCluster2] >= SLowestCoupling) {
1869 addCluster(iCluster2, coupledClusters, isClUsed, couplingClCl);
1875void ClusterFinderOriginal::extractLeastCoupledClusters(std::vector<int>& coupledClusters, std::vector<int>& clustersForFit,
1876 const std::vector<std::vector<double>>& couplingClCl)
const
1881 double minCoupling(DBL_MAX);
1882 int leastCoupledClusters[3] = {-1, -1, -1};
1885 std::vector<double> coupling1(coupledClusters.size(), 0.);
1886 for (
int i = 0;
i < coupledClusters.size(); ++
i) {
1887 for (
int j =
i + 1;
j < coupledClusters.size(); ++
j) {
1888 coupling1[
i] += couplingClCl[coupledClusters[
i]][coupledClusters[
j]];
1889 coupling1[
j] += couplingClCl[coupledClusters[
i]][coupledClusters[
j]];
1891 if (coupling1[
i] < minCoupling) {
1892 leastCoupledClusters[0] =
i;
1893 minCoupling = coupling1[
i];
1897 if (SNFitClustersMax > 1) {
1899 bool tryTierce = SNFitClustersMax > 2 && coupledClusters.size() > 5;
1901 for (
int i = 0;
i < coupledClusters.size(); ++
i) {
1902 for (
int j =
i + 1;
j < coupledClusters.size(); ++
j) {
1905 double coupling2 = coupling1[
i] + coupling1[
j] - 2. * couplingClCl[coupledClusters[
i]][coupledClusters[
j]];
1906 if (coupling2 < minCoupling) {
1907 leastCoupledClusters[0] =
i;
1908 leastCoupledClusters[1] =
j;
1909 leastCoupledClusters[2] = -1;
1910 minCoupling = coupling2;
1915 for (
int k =
j + 1; k < coupledClusters.size(); ++k) {
1916 double coupling3 = coupling2 + coupling1[k] -
1917 2. * (couplingClCl[coupledClusters[
i]][coupledClusters[k]] +
1918 couplingClCl[coupledClusters[
j]][coupledClusters[k]]);
1919 if (coupling3 < minCoupling) {
1920 leastCoupledClusters[0] =
i;
1921 leastCoupledClusters[1] =
j;
1922 leastCoupledClusters[2] = k;
1923 minCoupling = coupling3;
1934 for (
int i = 0; i < 3 && leastCoupledClusters[i] >= 0; ++
i) {
1935 clustersForFit.push_back(coupledClusters[leastCoupledClusters[
i] - idxShift]);
1936 coupledClusters.erase(coupledClusters.begin() + leastCoupledClusters[
i] - idxShift);
1942int ClusterFinderOriginal::selectPads(
const std::vector<int>& coupledClusters,
const std::vector<int>& clustersForFit,
1943 const std::vector<std::vector<double>>& couplingClPad)
1947 int nSelectedPads(0);
1949 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
1952 auto& pad = mPreCluster->pad(iPad);
1957 for (
int iCluster1 : clustersForFit) {
1960 if (couplingClPad[iCluster1][iPad] >= SLowestCoupling) {
1965 for (
int iCluster2 : coupledClusters) {
1966 if (couplingClPad[iCluster2][iPad] >= SLowestCoupling) {
1978 return nSelectedPads;
1982void ClusterFinderOriginal::merge(
const std::vector<int>& clustersForFit,
const std::vector<int>& coupledClusters,
1983 std::vector<std::vector<int>>& clustersOfPixels,
1984 std::vector<std::vector<double>>& couplingClCl,
1985 std::vector<std::vector<double>>& couplingClPad)
const
1989 for (
int iCluster1 : clustersForFit) {
1992 double maxCoupling(-1.);
1993 int iMostCoupled(0);
1994 for (
int iCluster2 : coupledClusters) {
1995 if (couplingClCl[iCluster1][iCluster2] > maxCoupling) {
1996 maxCoupling = couplingClCl[iCluster1][iCluster2];
1997 iMostCoupled = iCluster2;
2002 clustersOfPixels[iMostCoupled].insert(clustersOfPixels[iMostCoupled].
end(),
2003 clustersOfPixels[iCluster1].
begin(), clustersOfPixels[iCluster1].
end());
2006 for (
int iCluster2 : coupledClusters) {
2007 if (iCluster2 != iMostCoupled) {
2008 couplingClCl[iMostCoupled][iCluster2] += couplingClCl[iCluster1][iCluster2];
2009 couplingClCl[iCluster2][iMostCoupled] = couplingClCl[iMostCoupled][iCluster2];
2014 for (
int iPad = 0; iPad < mPreCluster->multiplicity(); ++iPad) {
2016 couplingClPad[iMostCoupled][iPad] += couplingClPad[iCluster1][iPad];
2023void ClusterFinderOriginal::updatePads(
const double fitParam[SNFitParamMax + 1],
int nParamUsed)
2028 double chargeFraction[SNFitClustersMax] = {0.};
2029 if (nParamUsed > 0) {
2030 param2ChargeFraction(fitParam, nParamUsed, chargeFraction);
2033 for (
auto& pad : *mPreCluster) {
2043 if (nParamUsed > 0) {
2044 double padChargeFit(0.);
2045 for (
int iParam = 0; iParam < nParamUsed; iParam += 3) {
2046 padChargeFit += chargeIntegration(fitParam[iParam], fitParam[iParam + 1], pad) * chargeFraction[iParam / 3];
2048 padChargeFit *= fitParam[SNFitParamMax];
2049 pad.setCharge(pad.charge() - padChargeFit);
2053 pad.setStatus((pad.charge() > mLowestPadCharge) ?
PadOriginal::kZero : PadOriginal::kOver);
2059void ClusterFinderOriginal::setClusterResolution(
Cluster& cluster)
const
2064 if (cluster.getChamberId() < 4) {
2073 int padIDNB(-1), padIDB(-1);
2077 auto itPadNB = mUsedDigits.end();
2078 if (padsFound || mSegmentation->
isValid(padIDNB)) {
2079 itPadNB = std::find_if(mUsedDigits.begin() + cluster.firstDigit, mUsedDigits.end(),
2080 [padIDNB](
const Digit& digit) { return digit.getPadID() == padIDNB; });
2082 auto itPadB = mUsedDigits.end();
2083 if (padsFound || mSegmentation->
isValid(padIDB)) {
2084 itPadB = std::find_if(mUsedDigits.begin() + cluster.firstDigit, mUsedDigits.end(),
2085 [padIDB](
const Digit& digit) { return digit.getPadID() == padIDB; });
Definition of a class to reconstruct clusters with the original MLEM algorithm.
Definition of the cluster used by the original cluster finder algorithm.
Configurable parameters for MCH clustering.
definition of the MCH processing errors
Original definition of the Mathieson function.
Definition of the pad used by the original cluster finder algorithm.
Configurable parameters for MCH charge induction and signal generation.
static const ClusterizerParam & Instance()
HMPID cluster implementation.
void init(bool run2Config)
void findClusters(gsl::span< const Digit > digits)
void add(ErrorType errorType, uint32_t id0, uint32_t id1, uint64_t n=1)
Original Mathieson function.
float integrate(float xMin, float yMin, float xMax, float yMax) const
@ kUseForFit
should be used for fit
@ kOver
processing is over
@ kMustKeep
do not kill (for pixels)
@ kCoupled
coupled to another cluster of pixels
void loadDigit(const Digit &digit)
void getPreClusters(std::vector< o2::mch::PreCluster > &preClusters, std::vector< Digit > &digits)
double padPositionX(int dePadIndex) const
double padSizeY(int dePadIndex) const
const CathodeSegmentation & nonBending() const
const CathodeSegmentation & bending() const
bool findPadPairByPosition(double x, double y, int &bpad, int &nbpad) const
bool isBendingPad(int dePadIndex) const
double padPositionY(int dePadIndex) const
double padSizeX(int dePadIndex) const
bool isValid(int dePadIndex) const
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
GLenum GLint GLint * precision
bool isValid(std::string_view dcsAlias)
O2MCHMAPPINGIMPL3_EXPORT const Segmentation & segmentation(int detElemId)
@ Clustering_TooManyLocalMaxima
bool areOverlapping(const PadOriginal &pad1, const PadOriginal &pad2, double precision)
auto findPad(std::vector< PadOriginal > &pads, double x, double y, double minCharge)
std::uniform_real_distribution< float > distX(-127.5, 127.5)
std::uniform_real_distribution< float > distY(-68., 68.)
Enum< T >::Iterator begin(Enum< T >)
Defining DataPointCompositeObject explicitly as copiable.
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< Digit > digits
char const *restrict const cmp