Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <algorithm>
#include "GPUO2InterfaceRefit.h"
#include "TPCFastTransform.h"
#include "DataFormatsTPC/PIDResponse.h"

namespace o2
{
Expand Down Expand Up @@ -65,6 +66,7 @@ class SVertexer
using Decay3BodyIndex = o2::dataformats::Decay3BodyIndex;
using RRef = o2::dataformats::RangeReference<int, int>;
using VBracket = o2::math_utils::Bracket<int>;
using PIDResponse = o2::tpc::PIDResponse;

enum HypV0 { Photon,
K0,
Expand Down Expand Up @@ -97,6 +99,9 @@ class SVertexer
GIndex gid{};
VBracket vBracket{};
float minR = 0; // track lowest point r
bool hasTPC = false;
uint8_t nITSclu = -1;
bool compatibleProton = false; // dE/dx compatibility with proton hypothesis (FIXME: use better, uint8_t compat mask?)
};

SVertexer(bool enabCascades = true, bool enab3body = false) : mEnableCascades{enabCascades}, mEnable3BodyDecays{enab3body}
Expand Down Expand Up @@ -183,6 +188,9 @@ class SVertexer
std::vector<DCAFitterN<2>> mFitterV0;
std::vector<DCAFitterN<2>> mFitterCasc;
std::vector<DCAFitterN<3>> mFitter3body;

PIDResponse mPIDresponse;

int mNThreads = 1;
int mNV0s = 0, mNCascades = 0, mN3Bodies = 0, mNStrangeTracks = 0;
float mMinR2ToMeanVertex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct SVertexerParams : public o2::conf::ConfigurableParamHelper<SVertexerParam
float maxDCAXYToMeanVertexV0Casc = 2.; ///< max DCA of V0 from beam line (mean vertex) for cascade V0 candidates
float maxDCAXYToMeanVertex3bodyV0 = 2; ///< max DCA of V0 from beam line (mean vertex) for 3body V0 candidates
float minPtV0 = 0.01; ///< v0 minimum pT
float minPtV0FromCascade = 0.3; ///< v0 minimum pT for v0 to be used in cascading (lowest pT Run 2 lambda: 0.4)
float maxTglV0 = 2.; ///< maximum tgLambda of V0

float causalityRTolerance = 1.; ///< V0 radius cannot exceed its contributors minR by more than this value
Expand Down Expand Up @@ -94,6 +95,18 @@ struct SVertexerParams : public o2::conf::ConfigurableParamHelper<SVertexerParam
float minTPCdEdx = 250; // starting from this dEdx value, tracks with p > minMomTPCdEdx are always accepted
float minMomTPCdEdx = 0.8; // minimum p for tracks with dEdx > mMinTPCdEdx to be accepted

uint8_t mITSSAminNclu = 6; // global requirement of at least this many ITS clusters if no TPC info present (N.B.: affects all secondary vertexing)
uint8_t mITSSAminNcluCascades = 6; // require at least this many ITS clusters if no TPC info present for cascade finding.
bool mRequireTPCforCascBaryons = true; // require that baryon daughter of cascade has TPC
bool mSkipTPCOnlyCascade = true; // skip TPC only tracks when doing cascade finding
bool mSkipTPCOnly3Body = true; // skip TPC only tracks when doing cascade finding

// percent deviation from expected proton dEdx - to be replaced - estimated sigma from TPC for now 6%; a 6*sigma cut is therefore 36% = 0.36f. Any value above 1.0f will be ignored manually when checking.
float mFractiondEdxforCascBaryons = 0.36f;
// default: average 2023 from C. Sonnabend, Nov 2023: ([0.217553 4.02762 0.00850178 2.33324 0.880904 ])
// to-do: grab from CCDB when available -> discussion with TPC experts, not available yet
float mBBpars[5] = {0.217553, 4.02762, 0.00850178, 2.33324, 0.880904};

// cuts on different V0 PID params
bool checkV0Hypothesis = true;
float pidCutsPhoton[SVertexHypothesis::NPIDParams] = {0.001, 20, 0.60, 20, 0.60, 0.0, 0.0, 0.0, 0.0}; // Photon
Expand Down
48 changes: 41 additions & 7 deletions Detectors/Vertexing/src/SVertexer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ void SVertexer::updateTimeDependentParams()
for (auto& ft : mFitter3body) {
ft.setBz(bz);
}

mPIDresponse.setBetheBlochParams(mSVParams->mBBpars);
}

//______________________________________________
Expand Down Expand Up @@ -429,6 +431,7 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a
auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
bool isTPCloaded = recoData.isTrackSourceLoaded(GIndex::TPC);
bool isITSloaded = recoData.isTrackSourceLoaded(GIndex::ITS);
if (isTPCloaded && !mSVParams->mExcludeTPCtracks) {
mTPCTracksArray = recoData.getTPCTracks();
mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs();
Expand Down Expand Up @@ -477,15 +480,32 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a
}
const auto& trc = recoData.getTrackParam(tvid);

bool hasTPC = false;
bool heavyIonisingParticle = false;
bool compatibleWithProton = mSVParams->mFractiondEdxforCascBaryons > 0.999f; // if 1 or above, accept all regardless of TPC
auto tpcGID = recoData.getTPCContributorGID(tvid);
if (tpcGID.isIndexSet() && isTPCloaded) {
hasTPC = true;
auto& tpcTrack = recoData.getTPCTrack(tpcGID);
float dEdxTPC = tpcTrack.getdEdx().dEdxTotTPC;
if (dEdxTPC > mSVParams->minTPCdEdx && trc.getP() > mSVParams->minMomTPCdEdx) // accept high dEdx tracks (He3, He4)
{
heavyIonisingParticle = true;
}
auto protonId = o2::track::PID::Proton;
float dEdxExpected = mPIDresponse.getExpectedSignal(tpcTrack, protonId);
float fracDevProton = std::abs((dEdxTPC - dEdxExpected) / dEdxExpected);
if (fracDevProton < mSVParams->mFractiondEdxforCascBaryons) {
compatibleWithProton = true;
}
}

// get Nclusters in the ITS if available
uint8_t nITSclu = -1;
auto itsGID = recoData.getITSContributorGID(tvid);
if (itsGID.getSource() == GIndex::ITS && isITSloaded) {
auto& itsTrack = recoData.getITSTrack(itsGID);
nITSclu = itsTrack.getNumberOfClusters();
}

if (!acceptTrack(tvid, trc) && !heavyIonisingParticle) {
Expand All @@ -494,9 +514,14 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a
}
continue;
}

if (!hasTPC && nITSclu < mSVParams->mITSSAminNclu) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you wanted to avoid using these tracks in the cascades only. We don't have a problem with V0 finding timing and rejecting on this level may affect K0s efficiency.

continue; // reject short ITS-only
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ddobrigk why not doing this rejection at least configurable, to have a possibility to study the difference between 1 and 2?

Copy link
Contributor Author

@ddobrigk ddobrigk Nov 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahor02 what do you mean? If you set mITSSAminNclu to zero you don't cut on anything ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahor02 what we could do if you like is to have an extra option to apply this solely to the cascade finding. It does seem like a bit of overkill to still keep very short and possibly very bad (in quality and especially p reso) ITS tracks, but if you want it, I can add an option, let me know. Thanks! :-D

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean to have a possibility to reject short ITS-only tracks in cascades but to accept them in V0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what we could do if you like is to have an extra option to apply this solely to the cascade finding. It does seem like a bit of overkill to still keep very short and possibly very bad (in quality and especially p reso) ITS tracks, but if you want it, I can add an option, let me know. Thanks! :-D

Yes, this is what I meant. Even if we don't use this option, it is worth having it evaluate the eff. loss.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahor02 ok, done! Thanks!

}

int posneg = trc.getSign() < 0 ? 1 : 0;
float r = std::sqrt(trc.getX() * trc.getX() + trc.getY() * trc.getY());
mTracksPool[posneg].emplace_back(TrackCand{trc, tvid, {iv, iv}, r});
mTracksPool[posneg].emplace_back(TrackCand{trc, tvid, {iv, iv}, r, hasTPC, nITSclu, compatibleWithProton});
if (tvid.getSource() == GIndex::TPC) { // constrained TPC track?
correctTPCTrack(mTracksPool[posneg].back(), mTPCTracksArray[tvid], -1, -1);
}
Expand Down Expand Up @@ -580,11 +605,15 @@ bool SVertexer::checkV0(const TrackCand& seedP, const TrackCand& seedN, int iP,
}
// check tight lambda mass only
bool goodLamForCascade = false, goodALamForCascade = false;
if (mV0Hyps[Lambda].checkTight(p2Pos, p2Neg, p2V0, ptV0)) {
goodLamForCascade = true;
}
if (mV0Hyps[AntiLambda].checkTight(p2Pos, p2Neg, p2V0, ptV0)) {
goodALamForCascade = true;
bool usesTPCOnly = (seedP.hasTPC && seedP.nITSclu < 1) || (seedN.hasTPC && seedN.nITSclu < 1);
bool usesShortITSOnly = (!seedP.hasTPC && seedP.nITSclu < mSVParams->mITSSAminNcluCascades) || (!seedN.hasTPC && seedN.nITSclu < mSVParams->mITSSAminNcluCascades);
if (ptV0 > mSVParams->minPtV0FromCascade && (!mSVParams->mSkipTPCOnlyCascade || !usesTPCOnly) && !usesShortITSOnly) {
if (mV0Hyps[Lambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedP.hasTPC) && seedP.compatibleProton) {
goodLamForCascade = true;
}
if (mV0Hyps[AntiLambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedN.hasTPC && seedN.compatibleProton)) {
goodALamForCascade = true;
}
}

// apply mass selections for 3-body decay
Expand All @@ -598,7 +627,7 @@ bool SVertexer::checkV0(const TrackCand& seedP, const TrackCand& seedN, int iP,
}

// we want to reconstruct the 3 body decay of hypernuclei starting from the V0 of a proton and a pion (e.g. H3L->d + (p + pi-), or He4L->He3 + (p + pi-)))
bool checkFor3BodyDecays = mEnable3BodyDecays && (!mSVParams->checkV0Hypothesis || good3bodyV0Hyp) && (pt2V0 > 0.5);
bool checkFor3BodyDecays = mEnable3BodyDecays && (!mSVParams->checkV0Hypothesis || good3bodyV0Hyp) && (pt2V0 > 0.5) && (!mSVParams->mSkipTPCOnly3Body || !usesTPCOnly);
bool rejectAfter3BodyCheck = false; // To reject v0s which can be 3-body decay candidates but not cascade or v0
bool checkForCascade = mEnableCascades && r2v0 < mMaxR2ToMeanVertexCascV0 && (!mSVParams->checkV0Hypothesis || (goodLamForCascade || goodALamForCascade));
bool rejectIfNotCascade = false;
Expand Down Expand Up @@ -746,6 +775,11 @@ int SVertexer::checkCascades(const V0Index& v0Idx, const V0& v0, float rv0, std:
continue; // skip the track used by V0
}
auto& bach = tracks[it];

if (!bach.hasTPC && bach.nITSclu < mSVParams->mITSSAminNcluCascades) {
continue; // reject short ITS-only
}

if (bach.vBracket.getMin() > v0vlist.getMax()) {
LOG(debug) << "Skipping";
break; // all other bachelor candidates will be also not compatible with this PV
Expand Down