@@ -36,8 +36,9 @@ MetadataHelper metadataInfo; // Metadata helper
3636using BCsWithRun2InfosTimestampsAndMatches = soa::Join<aod::BCs, aod::Run2BCInfos, aod::Timestamps, aod::Run2MatchedToBCSparse>;
3737using BCsWithRun3Matchings = soa::Join<aod::BCs, aod::Timestamps, aod::Run3MatchedToBCSparse>;
3838using BCsWithBcSelsRun2 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels, aod::Run2BCInfos, aod::Run2MatchedToBCSparse>;
39- using BCsWithBcSelsRun3 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels>;
39+ using BCsWithBcSelsRun3 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels, aod::Run3MatchedToBCSparse >;
4040using FullTracksIU = soa::Join<aod::TracksIU, aod::TracksExtra>;
41+ const double bcNS = o2::constants::lhc::LHCBunchSpacingNS;
4142
4243struct BcSelectionTask {
4344 Produces<aod::BcSels> bcsel;
@@ -454,6 +455,7 @@ struct EventSelectionTask {
454455 Configurable<float > confTimeRangeVetoOnCollStandard{" TimeRangeVetoOnCollStandard" , 10 , " Exclusion of a collision if there are other collisions nearby, +/- us" };
455456 Configurable<float > confTimeRangeVetoOnCollNarrow{" TimeRangeVetoOnCollNarrow" , 4 , " Exclusion of a collision if there are other collisions nearby, +/- us" };
456457 Configurable<bool > confUseWeightsForOccupancyVariable{" UseWeightsForOccupancyEstimator" , 1 , " Use or not the delta-time weights for the occupancy estimator" };
458+ Configurable<int > confSigmaBCforHighPtTracks{" confSigmaBCforHighPtTracks" , 4 , " Custom sigma (in bcs) for collisions with high-pt tracks" };
457459
458460 Partition<aod::Tracks> tracklets = (aod::track::trackType == static_cast <uint8_t >(o2::aod::track::TrackTypeEnum::Run2Tracklet));
459461
@@ -480,6 +482,40 @@ struct EventSelectionTask {
480482 return (dbc1 <= dbc2) ? index1 : index2;
481483 }
482484
485+ // helper function to find median time in the vector of TOF or TRD-track times
486+ float getMedian (std::vector<float > v)
487+ {
488+ int medianIndex = v.size () / 2 ;
489+ std::nth_element (v.begin (), v.begin () + medianIndex, v.end ());
490+ return v[medianIndex];
491+ }
492+
493+ // helper function to find closest TVX signal in time and in zVtx
494+ int64_t findBestGlobalBC (int64_t meanBC, int64_t sigmaBC, int32_t nContrib, float zVtxCol, std::map<int64_t , float >& mapGlobalBcVtxZ)
495+ {
496+ int64_t minBC = meanBC - 3 * sigmaBC;
497+ int64_t maxBC = meanBC + 3 * sigmaBC;
498+ // TODO: use ITS ROF bounds to reduce the search range?
499+
500+ float zVtxSigma = 2.7 * pow (nContrib, -0.466 ) + 0.024 ;
501+ zVtxSigma += 1.0 ; // additional uncertainty due to imperfectections of FT0 time calibration
502+
503+ auto itMin = mapGlobalBcVtxZ.lower_bound (minBC);
504+ auto itMax = mapGlobalBcVtxZ.upper_bound (maxBC);
505+
506+ float bestChi2 = 1e+10 ;
507+ int64_t bestGlobalBC = 0 ;
508+ for (std::map<int64_t , float >::iterator it = itMin; it != itMax; ++it) {
509+ float chi2 = pow ((it->second - zVtxCol) / zVtxSigma, 2 ) + pow ((it->first - meanBC) / sigmaBC, 2 .);
510+ if (chi2 < bestChi2) {
511+ bestChi2 = chi2;
512+ bestGlobalBC = it->first ;
513+ }
514+ }
515+
516+ return bestGlobalBC;
517+ }
518+
483519 void init (InitContext&)
484520 {
485521 if (metadataInfo.isFullyDefined ()) { // Check if the metadata is initialized (only if not forced from the workflow configuration)
@@ -501,7 +537,6 @@ struct EventSelectionTask {
501537 }
502538 }
503539
504- // ccdb->setURL("http://ccdb-test.cern.ch:8080");
505540 ccdb->setURL (" http://alice-ccdb.cern.ch" );
506541 ccdb->setCaching (true );
507542 ccdb->setLocalObjectValidityChecking ();
@@ -517,7 +552,7 @@ struct EventSelectionTask {
517552 evsel.reserve (collisions.size ());
518553 }
519554
520- void processRun2 (aod::Collision const & col, BCsWithBcSelsRun2 const &, aod::Tracks const &, aod::FV0Cs const &)
555+ void processRun2 (aod::Collision const & col, BCsWithBcSelsRun2 const &, FullTracksIU const &, aod::FV0Cs const &)
521556 {
522557 auto bc = col.bc_as <BCsWithBcSelsRun2>();
523558 EventSelectionParams* par = ccdb->getForTimeStamp <EventSelectionParams>(" EventSelection/EventSelectionParams" , bc.timestamp ());
@@ -589,8 +624,8 @@ struct EventSelectionTask {
589624 }
590625 PROCESS_SWITCH (EventSelectionTask, processRun2, " Process Run2 event selection" , true );
591626
592- Preslice <FullTracksIU> perCollision = aod::track::collisionId ;
593- void processRun3 (aod::Collisions const & cols, FullTracksIU const & tracks , BCsWithBcSelsRun3 const & bcs, aod::FT0s const &)
627+ Partition <FullTracksIU> pvTracks = (( aod::track::flags & ( uint32_t )o2::aod::track::PVContributor) == ( uint32_t )o2::aod::track::PVContributor) ;
628+ void processRun3 (aod::Collisions const & cols, FullTracksIU const &, BCsWithBcSelsRun3 const & bcs, aod::FT0s const &)
594629 {
595630 int run = bcs.iteratorAt (0 ).runNumber ();
596631 // extract bc pattern from CCDB for data or anchored MC only
@@ -600,7 +635,6 @@ struct EventSelectionTask {
600635 auto grplhcif = ccdb->getForTimeStamp <o2::parameters::GRPLHCIFData>(" GLO/Config/GRPLHCIF" , ts);
601636 bcPatternB = grplhcif->getBunchFilling ().getBCPattern ();
602637
603- //
604638 EventSelectionParams* par = ccdb->getForTimeStamp <EventSelectionParams>(" EventSelection/EventSelectionParams" , ts);
605639 // access orbit-reset timestamp
606640 auto ctpx = ccdb->getForTimeStamp <std::vector<Long64_t>>(" CTP/Calib/OrbitReset" , ts);
@@ -621,26 +655,24 @@ struct EventSelectionTask {
621655 nBCsPerTF = nOrbitsPerTF * o2::constants::lhc::LHCMaxBunches;
622656 }
623657
624- // create maps from globalBC to bc index for TVX or FT0-OR fired bcs
625- // to be used for closest TVX (FT0-OR) searches
658+ // create maps from globalBC to bc index for TVX- fired bcs
659+ // to be used for closest TVX searches
626660 std::map<int64_t , int32_t > mapGlobalBcWithTVX;
627- std::map<int64_t , int32_t > mapGlobalBcWithTOR ;
661+ std::map<int64_t , float > mapGlobalBcVtxZ ;
628662 for (auto & bc : bcs) {
629663 int64_t globalBC = bc.globalBC ();
630664 // skip non-colliding bcs for data and anchored runs
631665 if (run >= 500000 && bcPatternB[globalBC % o2::constants::lhc::LHCMaxBunches] == 0 ) {
632666 continue ;
633667 }
634- if (bc.selection_bit (kIsBBT0A ) || bc.selection_bit (kIsBBT0C )) {
635- mapGlobalBcWithTOR[globalBC] = bc.globalIndex ();
636- }
637668 if (bc.selection_bit (kIsTriggerTVX )) {
638669 mapGlobalBcWithTVX[globalBC] = bc.globalIndex ();
670+ mapGlobalBcVtxZ[globalBC] = bc.has_ft0 () ? bc.ft0 ().posZ () : 0 ;
639671 }
640672 }
641673
642674 // protection against empty FT0 maps
643- if (mapGlobalBcWithTOR. size () == 0 || mapGlobalBcWithTVX.size () == 0 ) {
675+ if (mapGlobalBcWithTVX.size () == 0 ) {
644676 LOGP (error, " FT0 table is empty or corrupted. Filling evsel table with dummy values" );
645677 for (auto & col : cols) {
646678 auto bc = col.bc_as <BCsWithBcSelsRun3>();
@@ -653,88 +685,139 @@ struct EventSelectionTask {
653685 }
654686 return ;
655687 }
656-
657- std::vector<int > vFoundBCindex (cols.size (), -1 ); // indices of found bcs
658- std::vector<bool > vIsVertexITSTPC (cols.size (), 0 ); // at least one of vertex contributors is ITS-TPC track
659- std::vector<bool > vIsVertexTOFmatched (cols.size (), 0 ); // at least one of vertex contributors is matched to TOF
660- std::vector<bool > vIsVertexTRDmatched (cols.size (), 0 ); // at least one of vertex contributors is matched to TRD
661- std::vector<int > vCollisionsPerBc (bcs.size (), 0 ); // counter of collisions per found bc for pileup checks
662-
663- // for the occupancy study
664- std::vector<int64_t > vFoundGlobalBC (cols.size (), 0 ); // global BCs for collisions
665688 std::vector<int > vTracksITS567perColl (cols.size (), 0 ); // counter of tracks per found bc for occupancy studies
666689 std::vector<bool > vIsFullInfoForOccupancy (cols.size (), 0 ); // info for occupancy in +/- windows is available (i.e. a given coll is not too close to the TF borders)
667690 const float timeWinOccupancyCalcMinNS = confTimeIntervalForOccupancyCalculationMin * 1e3 ; // ns
668691 const float timeWinOccupancyCalcMaxNS = confTimeIntervalForOccupancyCalculationMax * 1e3 ; // ns
669- // const double timeWinOccupancyExclusionRangeNS = confExclusionIntervalForOccupancyCalculation * 1e3; // ns
670- const double bcNS = o2::constants::lhc::LHCBunchSpacingNS;
671-
672- // loop to find nearest bc with FT0 entry -> foundBC index
692+ std::vector<bool > vIsVertexITSTPC (cols.size (), 0 ); // at least one of vertex contributors is ITS-TPC track
693+ std::vector<bool > vIsVertexTOFmatched (cols.size (), 0 ); // at least one of vertex contributors is matched to TOF
694+ std::vector<bool > vIsVertexTRDmatched (cols.size (), 0 ); // at least one of vertex contributors is matched to TRD
695+
696+ std::vector<int > vCollisionsPerBc (bcs.size (), 0 ); // counter of collisions per found bc for pileup checks
697+ std::vector<int > vFoundBCindex (cols.size (), -1 ); // indices of found bcs
698+ std::vector<int64_t > vFoundGlobalBC (cols.size (), 0 ); // global BCs for collisions
699+
700+ std::vector<bool > vIsVertexTOF (cols.size (), 0 );
701+ std::vector<bool > vIsVertexTRD (cols.size (), 0 );
702+ std::vector<bool > vIsVertexTPC (cols.size (), 0 );
703+ std::vector<bool > vIsVertexHighPtTPC (cols.size (), 0 );
704+ std::vector<int > vNcontributors (cols.size (), 0 );
705+ std::vector<float > vWeightedTimesTPCnoTOFnoTRD (cols.size (), 0 );
706+ std::vector<float > vWeightedSigmaTPCnoTOFnoTRD (cols.size (), 0 );
707+
708+ // temporary vectors to find tracks with median time
709+ std::vector<float > vTrackTimesTOF;
710+ std::vector<float > vTrackTimesTRDnoTOF;
711+
712+ // first loop to match collisions to TVX
673713 for (auto & col : cols) {
714+ int32_t colIndex = col.globalIndex ();
674715 auto bc = col.bc_as <BCsWithBcSelsRun3>();
675- int64_t meanBC = bc.globalBC ();
676- const double bcNS = o2::constants::lhc::LHCBunchSpacingNS;
677- int64_t deltaBC = std::ceil (col.collisionTimeRes () / bcNS * 4 );
678-
679- // count tracks of different types
680- int nITS567cls = 0 ;
681- int nITSTPCtracks = 0 ;
682- int nTOFtracks = 0 ;
683- int nTRDtracks = 0 ;
684- double timeFromTOFtracks = 0 ;
685- auto tracksGrouped = tracks.sliceBy (perCollision, col.globalIndex ());
686- for (auto & track : tracksGrouped) {
687- if (!track.isPVContributor ()) {
688- continue ;
689- }
690- nITSTPCtracks += track.hasITS () && track.hasTPC ();
691- nTOFtracks += track.hasTOF ();
692- nTRDtracks += track.hasTRD ();
693- // calculate average time using TOF tracks
694- if (track.hasTOF ()) {
695- timeFromTOFtracks += track.trackTime ();
696- }
716+ int64_t globalBC = bc.globalBC ();
717+ int64_t bcInTF = (bc.globalBC () - bcSOR) % nBCsPerTF;
718+ vIsFullInfoForOccupancy[colIndex] = ((bcInTF - 300 ) * bcNS > -timeWinOccupancyCalcMinNS) && ((nBCsPerTF - 4000 - bcInTF) * bcNS > timeWinOccupancyCalcMaxNS) ? true : false ;
697719
720+ const auto & colPvTracks = pvTracks.sliceByCached (aod::track::collisionId, col.globalIndex (), cache);
721+ vTrackTimesTOF.clear ();
722+ vTrackTimesTRDnoTOF.clear ();
723+ int nPvTracksTPCnoTOFnoTRD = 0 ;
724+ int nPvTracksHighPtTPCnoTOFnoTRD = 0 ;
725+ float sumTime = 0 , sumW = 0 , sumHighPtTime = 0 , sumHighPtW = 0 ;
726+ for (auto & track : colPvTracks) {
727+ float trackTime = track.trackTime ();
698728 if (track.itsNCls () >= 5 )
699- nITS567cls++;
729+ vTracksITS567perColl[colIndex]++;
730+ if (track.hasTRD ())
731+ vIsVertexTRDmatched[colIndex] = 1 ;
732+ if (track.hasTPC ())
733+ vIsVertexITSTPC[colIndex] = 1 ;
734+ if (track.hasTOF ()) {
735+ vTrackTimesTOF.push_back (trackTime);
736+ vIsVertexTOFmatched[colIndex] = 1 ;
737+ } else if (track.hasTRD ()) {
738+ vTrackTimesTRDnoTOF.push_back (trackTime);
739+ } else if (track.hasTPC ()) {
740+ float trackTimeRes = track.trackTimeRes ();
741+ float trackPt = track.pt ();
742+ float w = 1 . / (trackTimeRes * trackTimeRes);
743+ sumTime += trackTime * w;
744+ sumW += w;
745+ nPvTracksTPCnoTOFnoTRD++;
746+ if (trackPt > 1 ) {
747+ sumHighPtTime += trackTime * w;
748+ sumHighPtW += w;
749+ nPvTracksHighPtTPCnoTOFnoTRD++;
750+ }
751+ }
700752 }
701- LOGP (debug, " nContrib={} nITSTPCtracks={} nTOFtracks={} nTRDtracks={}" , col.numContrib (), nITSTPCtracks, nTOFtracks, nTRDtracks);
702-
703- if (nTOFtracks > 0 ) {
704- meanBC += TMath::FloorNint (timeFromTOFtracks / nTOFtracks / bcNS); // assign collision bc using TOF-matched tracks
705- deltaBC = 4 ; // use precise bc from TOF tracks with +/-4 bc margin
706- } else if (nITSTPCtracks > 0 ) {
707- deltaBC += 30 ; // extend deltaBC for collisions built with ITS-TPC tracks only
753+ vWeightedTimesTPCnoTOFnoTRD[colIndex] = sumW > 0 ? sumTime / sumW : 0 ;
754+ vWeightedSigmaTPCnoTOFnoTRD[colIndex] = sumW > 0 ? sqrt (1 . / sumW) : 0 ;
755+ vNcontributors[colIndex] = colPvTracks.size ();
756+ int nPvTracksTOF = vTrackTimesTOF.size ();
757+ int nPvTracksTRDnoTOF = vTrackTimesTRDnoTOF.size ();
758+ // collision type
759+ vIsVertexTOF[colIndex] = nPvTracksTOF > 0 ;
760+ vIsVertexTRD[colIndex] = nPvTracksTRDnoTOF > 0 ;
761+ vIsVertexTPC[colIndex] = nPvTracksTPCnoTOFnoTRD > 0 ;
762+ vIsVertexHighPtTPC[colIndex] = nPvTracksHighPtTPCnoTOFnoTRD > 0 ;
763+
764+ int64_t foundGlobalBC = 0 ;
765+ int32_t foundBCindex = -1 ;
766+
767+ if (nPvTracksTOF > 0 ) {
768+ // for collisions with TOF tracks:
769+ // take bc corresponding to TOF track with median time
770+ int64_t tofGlobalBC = globalBC + TMath::Nint (getMedian (vTrackTimesTOF) / bcNS);
771+ std::map<int64_t , int32_t >::iterator it = mapGlobalBcWithTVX.find (tofGlobalBC);
772+ if (it != mapGlobalBcWithTVX.end ()) {
773+ foundGlobalBC = it->first ;
774+ foundBCindex = it->second ;
775+ }
776+ } else if (nPvTracksTPCnoTOFnoTRD == 0 && nPvTracksTRDnoTOF > 0 ) {
777+ // for collisions with TRD tracks but without TOF or ITSTPC-only tracks:
778+ // take bc corresponding to TRD track with median time
779+ int64_t trdGlobalBC = globalBC + TMath::Nint (getMedian (vTrackTimesTRDnoTOF) / bcNS);
780+ std::map<int64_t , int32_t >::iterator it = mapGlobalBcWithTVX.find (trdGlobalBC);
781+ if (it != mapGlobalBcWithTVX.end ()) {
782+ foundGlobalBC = it->first ;
783+ foundBCindex = it->second ;
784+ }
785+ } else if (nPvTracksHighPtTPCnoTOFnoTRD > 0 ) {
786+ // for collisions with high-pt ITSTPC-nonTOF-nonTRD tracks
787+ // search in 3*confSigmaBCforHighPtTracks range (3*4 bcs by default)
788+ int64_t meanBC = globalBC + TMath::Nint (sumHighPtTime / sumHighPtW / bcNS);
789+ int64_t bestGlobalBC = findBestGlobalBC (meanBC, confSigmaBCforHighPtTracks, vNcontributors[colIndex], col.posZ (), mapGlobalBcVtxZ);
790+ if (bestGlobalBC > 0 ) {
791+ foundGlobalBC = bestGlobalBC;
792+ foundBCindex = mapGlobalBcWithTVX[bestGlobalBC];
793+ }
708794 }
709795
710- int64_t minBC = meanBC - deltaBC;
711- int64_t maxBC = meanBC + deltaBC;
796+ // fill foundBC indices and global BCs
797+ // keep current bc if TVX matching failed at this step
798+ vFoundBCindex[colIndex] = foundBCindex >= 0 ? foundBCindex : bc.globalIndex ();
799+ vFoundGlobalBC[colIndex] = foundGlobalBC > 0 ? foundGlobalBC : globalBC;
712800
713- int32_t indexClosestTVX = findClosest (meanBC, mapGlobalBcWithTVX);
714- int64_t tvxBC = bcs.iteratorAt (indexClosestTVX).globalBC ();
715- if (tvxBC >= minBC && tvxBC <= maxBC) { // closest TVX within search region
716- bc.setCursor (indexClosestTVX);
717- } else { // no TVX within search region, searching for TOR = T0A | T0C
718- int32_t indexClosestTOR = findClosest (meanBC, mapGlobalBcWithTOR);
719- int64_t torBC = bcs.iteratorAt (indexClosestTOR).globalBC ();
720- if (torBC >= minBC && torBC <= maxBC) {
721- bc.setCursor (indexClosestTOR);
722- }
723- }
724- int32_t foundBC = bc.globalIndex ();
801+ // erase found global BC with TVX from the pool of bcs for the next loop over low-pt TPCnoTOFnoTRD collisions
802+ if (foundBCindex >= 0 )
803+ mapGlobalBcVtxZ.erase (foundGlobalBC);
804+ }
805+
806+ // second loop to match remaining low-pt TPCnoTOFnoTRD collisions
807+ for (auto & col : cols) {
725808 int32_t colIndex = col.globalIndex ();
726- LOGP (debug, " foundBC = {} globalBC = {} " , foundBC, bc. globalBC ());
727- vFoundBCindex[colIndex] = foundBC ;
728- vIsVertexITSTPC[colIndex] = nITSTPCtracks > 0 ;
729- vIsVertexTOFmatched[colIndex] = nTOFtracks > 0 ;
730- vIsVertexTRDmatched[colIndex] = nTRDtracks > 0 ;
731- vCollisionsPerBc[foundBC]++ ;
732- vTracksITS567perColl [colIndex] = nITS567cls ;
733- vFoundGlobalBC[colIndex] = bc. globalBC () ;
734-
735- // check that this collision has full information inside the time window (taking into account TF borders)
736- int64_t bcInTF = (bc. globalBC () - bcSOR) % nBCsPerTF;
737- vIsFullInfoForOccupancy[ colIndex] = ((bcInTF - 300 ) * bcNS > -timeWinOccupancyCalcMinNS) && ((nBCsPerTF - 4000 - bcInTF) * bcNS > timeWinOccupancyCalcMaxNS) ? true : false ;
809+ if (vIsVertexTPC[colIndex] > 0 && vIsVertexHighPtTPC[colIndex] == 0 ) {
810+ float weightedTime = vWeightedTimesTPCnoTOFnoTRD[colIndex] ;
811+ float weightedSigma = vWeightedSigmaTPCnoTOFnoTRD[colIndex] ;
812+ auto bc = col. bc_as <BCsWithBcSelsRun3>() ;
813+ int64_t globalBC = bc. globalBC () ;
814+ int64_t meanBC = globalBC + TMath::Nint (weightedTime / bcNS) ;
815+ int64_t bestGlobalBC = findBestGlobalBC (meanBC, weightedSigma / bcNS, vNcontributors [colIndex], col. posZ (), mapGlobalBcVtxZ) ;
816+ vFoundGlobalBC[colIndex] = bestGlobalBC > 0 ? bestGlobalBC : globalBC;
817+ vFoundBCindex[colIndex] = bestGlobalBC > 0 ? mapGlobalBcWithTVX[bestGlobalBC] : bc. globalIndex ();
818+ }
819+ // fill pileup counter
820+ vCollisionsPerBc[vFoundBCindex[ colIndex]]++ ;
738821 }
739822
740823 // save indices of collisions in time range for occupancy calculation
@@ -817,7 +900,7 @@ struct EventSelectionTask {
817900 int nITS567tracksInTimeBins[nTimeIntervals] = {};
818901 int nITS567tracksForVetoStandard = 0 ; // to veto events with nearby collisions
819902 int nITS567tracksForVetoNarrow = 0 ; // to veto events with nearby collisions (narrower range)
820- for (int iCol = 0 ; iCol < vAssocToThisCol.size (); iCol++) {
903+ for (uint32_t iCol = 0 ; iCol < vAssocToThisCol.size (); iCol++) {
821904 int thisColIndex = vAssocToThisCol[iCol];
822905 if (thisColIndex == colIndex) // skip the same collision
823906 continue ;
0 commit comments