30#ifdef _PERFORM_TIMING_ 
   50      const int MaxErrLog = 2;
 
   51      static int errLocCount = 0;
 
   52      if (errLocCount++ < MaxErrLog) {
 
   53        LOGP(warn, 
"Impossible ROF IR {}, does not exceed previous {}, discarding in clusterization", reader.
getInteractionRecord().
asString(), lastIR.asString());
 
   60    mFiredChipsPtr.clear();
 
   63      mFiredChipsPtr.push_back(curChipData);
 
   64      nPix += curChipData->
getData().size();
 
   67    auto& rof = vecROFRec->emplace_back(reader.
getInteractionRecord(), vecROFRec->size(), compClus->size(), 0); 
 
   69    uint16_t nFired = mFiredChipsPtr.size();
 
   76    if (nFired < nThreads) {
 
   82    uint16_t chipStep = nThreads > 1 ? (nThreads == 2 ? 20 : 10) : nFired;
 
   83    int dynGrp = std::min(4, std::max(1, nThreads / 2));
 
   84    if (nThreads > mThreads.size()) {
 
   85      int oldSz = mThreads.size();
 
   86      mThreads.resize(nThreads);
 
   87      for (
int i = oldSz; 
i < nThreads; 
i++) {
 
   88        mThreads[
i] = std::make_unique<ClustererThread>(
this, 
i);
 
   92#pragma omp parallel for schedule(dynamic, dynGrp) num_threads(nThreads) 
   94    for (uint16_t ic = 0; ic < nFired; ic += chipStep) {
 
   95      auto ith = omp_get_thread_num();
 
   97        mThreads[ith]->process(ic, std::min(chipStep, uint16_t(nFired - ic)),
 
   98                               &mThreads[ith]->compClusters,
 
   99                               patterns ? &mThreads[ith]->patterns : 
nullptr,
 
  101                               labelsCl ? &mThreads[ith]->labels : 
nullptr, rof);
 
  103        mThreads[0]->process(0, nFired, compClus, patterns, labelsCl ? reader.
getDigitsMCTruth() : 
nullptr, labelsCl, rof);
 
  108    mThreads[0]->process(0, nFired, compClus, patterns, labelsCl ? reader.
getDigitsMCTruth() : 
nullptr, labelsCl, rof);
 
  112#ifdef _PERFORM_TIMING_ 
  113      mTimerMerge.Start(
false);
 
  115      size_t nClTot = 0, nPattTot = 0;
 
  116      int chid = 0, thrStatIdx[nThreads];
 
  117      for (
int ith = 0; ith < nThreads; ith++) {
 
  118        std::sort(mThreads[ith]->stats.begin(), mThreads[ith]->stats.end(), [](
const ThreadStat& 
a, 
const ThreadStat& 
b) { return a.firstChip < b.firstChip; });
 
  120        nClTot += mThreads[ith]->compClusters.size();
 
  121        nPattTot += mThreads[ith]->patterns.size();
 
  123      compClus->reserve(nClTot);
 
  125        patterns->reserve(nPattTot);
 
  127      while (
chid < nFired) {
 
  128        for (
int ith = 0; ith < nThreads; ith++) {
 
  129          if (thrStatIdx[ith] >= mThreads[ith]->stats.size()) {
 
  132          const auto& stat = mThreads[ith]->stats[thrStatIdx[ith]];
 
  133          if (stat.firstChip == 
chid) {
 
  136            const auto clbeg = mThreads[ith]->compClusters.begin() + stat.firstClus;
 
  137            auto szold = compClus->size();
 
  138            compClus->insert(compClus->end(), clbeg, clbeg + stat.nClus);
 
  140              const auto ptbeg = mThreads[ith]->patterns.begin() + stat.firstPatt;
 
  141              patterns->insert(patterns->end(), ptbeg, ptbeg + stat.nPatt);
 
  144              labelsCl->
mergeAtBack(mThreads[ith]->labels, stat.firstClus, stat.nClus);
 
  149      for (
int ith = 0; ith < nThreads; ith++) {
 
  150        mThreads[ith]->patterns.clear();
 
  151        mThreads[ith]->compClusters.clear();
 
  152        mThreads[ith]->labels.clear();
 
  153        mThreads[ith]->stats.clear();
 
  155#ifdef _PERFORM_TIMING_ 
  159      mThreads[0]->stats.clear();
 
  161    rof.setNEntries(compClus->size() - rof.getFirstEntry()); 
 
  162  } 
while (autoDecode);
 
  164#ifdef _PERFORM_TIMING_ 
 
  173  if (
stats.empty() || 
stats.back().firstChip + 
stats.back().nChips != chip) { 
 
  174    stats.emplace_back(
ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0});
 
  176  for (
int ic = 0; ic < nChips; ic++) {
 
  177    auto* curChipData = 
parent->mFiredChipsPtr[chip + ic];
 
  178    auto chipID = curChipData->getChipID();
 
  179    if (
parent->mMaxBCSeparationToMask > 0) { 
 
  180      const auto& chipInPrevROF = 
parent->mChipsOld[chipID];
 
  182        parent->mMaxRowColDiffToMask ? curChipData->maskFiredInSample(
parent->mChipsOld[chipID], 
parent->mMaxRowColDiffToMask) : curChipData->maskFiredInSample(
parent->mChipsOld[chipID]);
 
  185    auto nclus0 = compClusPtr->size();
 
  186    auto validPixID = curChipData->getFirstUnmasked();
 
  187    auto npix = curChipData->getData().size();
 
  188    if (validPixID < npix) { 
 
  189      auto valp = validPixID++;
 
  190      if (validPixID == npix) { 
 
  194        for (; validPixID < npix; validPixID++) {
 
  195          if (!curChipData->getData()[validPixID].isMasked()) {
 
  199        finishChip(curChipData, compClusPtr, patternsPtr, labelsDigPtr, labelsClPtr);
 
  202    if (
parent->mMaxBCSeparationToMask > 0) { 
 
  203      parent->mChipsOld[chipID].swap(*curChipData);
 
  206  auto& currStat = 
stats.back();
 
  207  currStat.nChips += nChips;
 
  208  currStat.nClus = compClusPtr->size() - currStat.firstClus;
 
  209  currStat.nPatt = patternsPtr ? (patternsPtr->size() - currStat.firstPatt) : 0;
 
 
  216  const auto& pixData = curChipData->
getData();
 
  217  for (
int i1 = 0; i1 < preClusterHeads.size(); ++i1) {
 
  218    auto ci = preClusterIndices[i1];
 
  224    int next = preClusterHeads[i1];
 
  227      const auto& pixEntry = 
pixels[next];
 
  228      const auto pix = pixData[pixEntry.second];
 
  229      pixArrBuff.push_back(pix); 
 
  230      bbox.adjust(pix.getRowDirect(), pix.getCol());
 
  232        if (parent->mSquashingDepth) { 
 
  233          fetchMCLabels(curChipData->
getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
 
  235          fetchMCLabels(pixEntry.second + curChipData->
getStartID(), labelsDigPtr, nlab);
 
  238      next = pixEntry.first;
 
  240    preClusterIndices[i1] = -1;
 
  241    for (
int i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) {
 
  242      if (preClusterIndices[i2] != ci) {
 
  245      next = preClusterHeads[i2];
 
  247        const auto& pixEntry = 
pixels[next];
 
  248        const auto pix = pixData[pixEntry.second]; 
 
  249        pixArrBuff.push_back(pix);                 
 
  250        bbox.adjust(pix.getRowDirect(), pix.getCol());
 
  252          if (parent->mSquashingDepth) { 
 
  253            fetchMCLabels(curChipData->
getOrderedPixId(pixEntry.second), labelsDigPtr, nlab);
 
  255            fetchMCLabels(pixEntry.second + curChipData->
getStartID(), labelsDigPtr, nlab);
 
  258        next = pixEntry.first;
 
  260      preClusterIndices[i2] = -1;
 
  262    if (
bbox.isAcceptableSize()) {
 
  263      parent->streamCluster(pixArrBuff, &labelsBuff, 
bbox, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab);
 
  266      if (!parent->mDropHugeClusters) {
 
  268          LOGP(warn, 
"Splitting a huge cluster: chipID {}, rows {}:{} cols {}:{}{}", 
bbox.chipID, 
bbox.rowMin, 
bbox.rowMax, 
bbox.colMin, 
bbox.colMax,
 
  269               warnLeft == 1 ? 
" (Further warnings will be muted)" : 
"");
 
  274            parent->mNHugeClus++;
 
  278        std::vector<PixelData> pixbuf;
 
  284            for (
const auto& pix : pixArrBuff) {
 
  285              if (bboxT.
isInside(pix.getRowDirect(), pix.getCol())) {
 
  286                pixbuf.push_back(pix);
 
  289            if (!pixbuf.empty()) { 
 
  290              parent->streamCluster(pixbuf, &labelsBuff, bboxT, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, 
true);
 
 
  306  auto pix = curChipData->
getData()[hit];
 
  307  uint16_t 
row = pix.getRowDirect(), 
col = pix.getCol();
 
  311    fetchMCLabels(curChipData->
getStartID() + hit, labelsDigPtr, nlab);
 
  312    auto cnt = compClusPtr->size();
 
  313    for (
int i = nlab; 
i--;) {
 
  322    patternsPtr->emplace_back(1); 
 
  323    patternsPtr->emplace_back(1); 
 
  324    patternsPtr->insert(patternsPtr->end(), std::begin(patt), std::begin(patt) + 1);