Skip to content

Commit 8de76aa

Browse files
AlexBigOAlexandre Bigot
andauthored
PWGHF: Handle ambiguous tracks in B0 candidate creator and get bz from CCDB (#2280)
* Change structure: loop over aod::Collisions, over D candidates attached to this collision and over tracks attached to this collision * Repropagate 3prong daughters to this collision if needed + Apply fixes * PR comments * Modify initialization of dca for D daughters * Get bz from CCDB --------- Co-authored-by: Alexandre Bigot <abigot@sbgat402.in2p3.fr>
1 parent c334c17 commit 8de76aa

1 file changed

Lines changed: 204 additions & 111 deletions

File tree

PWGHF/TableProducer/candidateCreatorB0.cxx

Lines changed: 204 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
#include "Framework/AnalysisTask.h"
2020
#include "DCAFitter/DCAFitterN.h"
2121
#include "Common/Core/trackUtilities.h"
22+
#include "Common/DataModel/CollisionAssociation.h"
2223
#include "ReconstructionDataFormats/DCA.h"
2324
#include "ReconstructionDataFormats/V0.h"
2425
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
2526
#include "PWGHF/DataModel/CandidateSelectionTables.h"
27+
#include "PWGHF/Utils/utilsBfieldCCDB.h"
2628

2729
using namespace o2;
2830
using namespace o2::aod;
@@ -51,7 +53,7 @@ struct HfCandidateCreatorB0 {
5153
Produces<aod::HfCandB0Config> rowCandidateConfig;
5254

5355
// vertexing
54-
Configurable<double> bz{"bz", 5., "magnetic field"};
56+
// Configurable<double> bz{"bz", 5., "magnetic field"};
5557
Configurable<bool> propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"};
5658
Configurable<bool> useAbsDCA{"useAbsDCA", false, "Minimise abs. distance rather than chi2"};
5759
Configurable<bool> useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"};
@@ -68,16 +70,32 @@ struct HfCandidateCreatorB0 {
6870
Configurable<int> selectionFlagD{"selectionFlagD", 1, "Selection Flag for D"};
6971
// FIXME: store B0 creator configurable (until https://alice.its.cern.ch/jira/browse/O2-3582 solved)
7072
bool isHfCandB0ConfigFilled = false;
73+
// magnetic field setting from CCDB
74+
Configurable<bool> isRun2{"isRun2", false, "enable Run 2 or Run 3 GRP objects for magnetic field"};
75+
Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
76+
Configurable<std::string> ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"};
77+
Configurable<std::string> ccdbPathGeo{"ccdbPathGeo", "GLO/Config/GeometryAligned", "Path of the geometry file"};
78+
Configurable<std::string> ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"};
79+
Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"};
80+
81+
Service<o2::ccdb::BasicCCDBManager> ccdb;
82+
o2::base::MatLayerCylSet* lut;
83+
o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT;
84+
int runNumber;
7185

7286
double massPi = RecoDecay::getMassPDG(kPiPlus);
7387
double massD = RecoDecay::getMassPDG(pdg::Code::kDMinus);
7488
double massB0 = RecoDecay::getMassPDG(pdg::Code::kB0);
7589
double massDPi{0.};
90+
double bz{0.};
7691

7792
using TracksWithSel = soa::Join<aod::BigTracksExtended, aod::TrackSelection>;
7893

79-
Filter filterSelectTracks = (!usePionIsGlobalTrackWoDCA) || ((usePionIsGlobalTrackWoDCA) && requireGlobalTrackWoDCAInFilter());
8094
Filter filterSelectCandidates = (aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagD);
95+
using CandsDFiltered = soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi>>;
96+
Preslice<CandsDFiltered> candsDPerCollision = aod::track_association::collisionId;
97+
98+
Preslice<aod::TrackAssoc> trackIndicesPerCollision = aod::track_association::collisionId;
8199

82100
OutputObj<TH1F> hMassDToPiKPi{TH1F("hMassDToPiKPi", "D^{#minus} candidates;inv. mass (p^{#minus} K^{#plus} #pi^{#minus}) (GeV/#it{c}^{2});entries", 500, 0., 5.)};
83101
OutputObj<TH1F> hPtD{TH1F("hPtD", "D^{#minus} candidates;D^{#minus} candidate #it{p}_{T} (GeV/#it{c});entries", 100, 0., 10.)};
@@ -87,6 +105,18 @@ struct HfCandidateCreatorB0 {
87105
OutputObj<TH1F> hCovPVXX{TH1F("hCovPVXX", "2-prong candidates;XX element of cov. matrix of prim. vtx. position (cm^{2});entries", 100, 0., 1.e-4)};
88106
OutputObj<TH1F> hCovSVXX{TH1F("hCovSVXX", "2-prong candidates;XX element of cov. matrix of sec. vtx. position (cm^{2});entries", 100, 0., 0.2)};
89107

108+
void init(InitContext const&)
109+
{
110+
ccdb->setURL(ccdbUrl);
111+
ccdb->setCaching(true);
112+
ccdb->setLocalObjectValidityChecking();
113+
lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get<o2::base::MatLayerCylSet>(ccdbPathLut));
114+
if (!o2::base::GeometryManager::isGeometryLoaded()) {
115+
ccdb->get<TGeoManager>(ccdbPathGeo);
116+
}
117+
runNumber = 0;
118+
}
119+
90120
/// Single-track cuts for pions on dcaXY
91121
/// \param track is a track
92122
/// \return true if track passes all cuts
@@ -107,10 +137,11 @@ struct HfCandidateCreatorB0 {
107137
return true;
108138
}
109139

110-
void process(aod::Collision const& collision,
111-
soa::Filtered<soa::Join<aod::HfCand3Prong, aod::HfSelDplusToPiKPi>> const& candsD,
140+
void process(aod::Collisions const& collisions,
141+
CandsDFiltered const& candsD,
142+
aod::TrackAssoc const& trackIndices,
112143
TracksWithSel const&,
113-
soa::Filtered<TracksWithSel> const& tracksPion)
144+
aod::BCsWithTimestamps const&)
114145
{
115146
// FIXME: store B0 creator configurable (until https://alice.its.cern.ch/jira/browse/O2-3582 solved)
116147
if (!isHfCandB0ConfigFilled) {
@@ -120,7 +151,7 @@ struct HfCandidateCreatorB0 {
120151
}
121152
// Initialise fitter for B vertex (2-prong vertex filter)
122153
o2::vertexing::DCAFitterN<2> df2;
123-
df2.setBz(bz);
154+
// df2.setBz(bz);
124155
df2.setPropagateToPCA(propagateToPCA);
125156
df2.setMaxR(maxR);
126157
df2.setMaxDZIni(maxDZIni);
@@ -131,7 +162,7 @@ struct HfCandidateCreatorB0 {
131162

132163
// Initial fitter to redo D-vertex to get extrapolated daughter tracks (3-prong vertex filter)
133164
o2::vertexing::DCAFitterN<3> df3;
134-
df3.setBz(bz);
165+
// df3.setBz(bz);
135166
df3.setPropagateToPCA(propagateToPCA);
136167
df3.setMaxR(maxR);
137168
df3.setMaxDZIni(maxDZIni);
@@ -140,119 +171,181 @@ struct HfCandidateCreatorB0 {
140171
df3.setUseAbsDCA(useAbsDCA);
141172
df3.setWeightedFinalPCA(useWeightedFinalPCA);
142173

143-
// loop over D candidates
144-
for (const auto& candD : candsD) {
145-
hMassDToPiKPi->Fill(invMassDplusToPiKPi(candD), candD.pt());
146-
hPtD->Fill(candD.pt());
147-
hCPAD->Fill(candD.cpa());
148-
149-
// track0 <-> pi, track1 <-> K, track2 <-> pi
150-
auto track0 = candD.prong0_as<TracksWithSel>();
151-
auto track1 = candD.prong1_as<TracksWithSel>();
152-
auto track2 = candD.prong2_as<TracksWithSel>();
153-
auto trackParCov0 = getTrackParCov(track0);
154-
auto trackParCov1 = getTrackParCov(track1);
155-
auto trackParCov2 = getTrackParCov(track2);
156-
157-
// reconstruct 3-prong secondary vertex (D±)
158-
if (df3.process(trackParCov0, trackParCov1, trackParCov2) == 0) {
159-
continue;
160-
}
174+
static int ncol = 0;
161175

162-
const auto& secondaryVertex = df3.getPCACandidate();
163-
trackParCov0.propagateTo(secondaryVertex[0], bz);
164-
trackParCov1.propagateTo(secondaryVertex[0], bz);
165-
trackParCov2.propagateTo(secondaryVertex[0], bz);
166-
167-
// D∓ → π∓ K± π∓
168-
array<float, 3> pVecpiK = {track0.px() + track1.px(), track0.py() + track1.py(), track0.pz() + track1.pz()};
169-
array<float, 3> pVecD = {pVecpiK[0] + track2.px(), pVecpiK[1] + track2.py(), pVecpiK[2] + track2.pz()};
170-
auto trackParCovPiK = o2::dataformats::V0(df3.getPCACandidatePos(), pVecpiK, df3.calcPCACovMatrixFlat(),
171-
trackParCov0, trackParCov1, {0, 0}, {0, 0});
172-
auto trackParCovD = o2::dataformats::V0(df3.getPCACandidatePos(), pVecD, df3.calcPCACovMatrixFlat(),
173-
trackParCovPiK, trackParCov2, {0, 0}, {0, 0});
174-
175-
int index0D = track0.globalIndex();
176-
int index1D = track1.globalIndex();
177-
int index2D = track2.globalIndex();
178-
179-
// loop over pions
180-
for (const auto& trackPion : tracksPion) {
181-
// minimum pT selection
182-
if (trackPion.pt() < ptPionMin || !isSelectedTrackDCA(trackPion)) {
183-
continue;
176+
for (const auto& collision : collisions) {
177+
auto primaryVertex = getPrimaryVertex(collision);
178+
auto covMatrixPV = primaryVertex.getCov();
179+
180+
if (ncol % 10000 == 0) {
181+
LOG(debug) << ncol << " collisions parsed";
182+
}
183+
ncol++;
184+
185+
/// Set the magnetic field from ccdb.
186+
/// The static instance of the propagator was already modified in the HFTrackIndexSkimCreator,
187+
/// but this is not true when running on Run2 data/MC already converted into AO2Ds.
188+
auto bc = collision.bc_as<aod::BCsWithTimestamps>();
189+
if (runNumber != bc.runNumber()) {
190+
LOG(info) << ">>>>>>>>>>>> Current run number: " << runNumber;
191+
initCCDB(bc, runNumber, ccdb, isRun2 ? ccdbPathGrp : ccdbPathGrpMag, lut, isRun2);
192+
bz = o2::base::Propagator::Instance()->getNominalBz();
193+
LOG(info) << ">>>>>>>>>>>> Magnetic field: " << bz;
194+
}
195+
df2.setBz(bz);
196+
df3.setBz(bz);
197+
198+
auto thisCollId = collision.globalIndex();
199+
auto candsDThisColl = candsD.sliceBy(candsDPerCollision, thisCollId);
200+
201+
for (const auto& candD : candsDThisColl) { // start loop over filtered D candidates indices as associated to this collision in candidateCreator3Prong.cxx
202+
hMassDToPiKPi->Fill(invMassDplusToPiKPi(candD), candD.pt());
203+
hPtD->Fill(candD.pt());
204+
hCPAD->Fill(candD.cpa());
205+
206+
// track0 <-> pi, track1 <-> K, track2 <-> pi
207+
auto track0 = candD.prong0_as<TracksWithSel>();
208+
auto track1 = candD.prong1_as<TracksWithSel>();
209+
auto track2 = candD.prong2_as<TracksWithSel>();
210+
auto trackParCov0 = getTrackParCov(track0);
211+
auto trackParCov1 = getTrackParCov(track1);
212+
auto trackParCov2 = getTrackParCov(track2);
213+
214+
std::array<float, 3> pVec0 = {track0.px(), track0.py(), track0.pz()};
215+
std::array<float, 3> pVec1 = {track1.px(), track1.py(), track1.pz()};
216+
std::array<float, 3> pVec2 = {track2.px(), track2.py(), track2.pz()};
217+
218+
auto dca0 = o2::dataformats::DCA(track0.dcaXY(), track0.dcaZ(), track0.cYY(), track0.cZY(), track0.cZZ());
219+
auto dca1 = o2::dataformats::DCA(track1.dcaXY(), track1.dcaZ(), track1.cYY(), track1.cZY(), track1.cZZ());
220+
auto dca2 = o2::dataformats::DCA(track2.dcaXY(), track2.dcaZ(), track2.cYY(), track2.cZY(), track2.cZZ());
221+
222+
// repropagate tracks to this collision if needed
223+
if (track0.collisionId() != thisCollId) {
224+
trackParCov0.propagateToDCA(primaryVertex, bz, &dca0);
184225
}
185-
// reject pions that are D daughters
186-
if (trackPion.globalIndex() == index0D || trackPion.globalIndex() == index1D || trackPion.globalIndex() == index2D) {
187-
continue;
226+
227+
if (track1.collisionId() != thisCollId) {
228+
trackParCov1.propagateToDCA(primaryVertex, bz, &dca1);
188229
}
189-
// reject pi and D with same sign
190-
if (trackPion.sign() * track0.sign() > 0) {
191-
continue;
230+
231+
if (track2.collisionId() != thisCollId) {
232+
trackParCov2.propagateToDCA(primaryVertex, bz, &dca2);
192233
}
193234

194-
hPtPion->Fill(trackPion.pt());
195-
array<float, 3> pVecPion = {trackPion.px(), trackPion.py(), trackPion.pz()};
196-
auto trackParCovPi = getTrackParCov(trackPion);
197235
// ---------------------------------
198-
// reconstruct the 2-prong B0 vertex
199-
if (df2.process(trackParCovD, trackParCovPi) == 0) {
236+
// reconstruct 3-prong secondary vertex (D±)
237+
if (df3.process(trackParCov0, trackParCov1, trackParCov2) == 0) {
200238
continue;
201239
}
202240

203-
// calculate invariant mass
204-
massDPi = RecoDecay::m(array{pVecD, pVecPion}, array{massD, massPi});
205-
if (std::abs(massDPi - massB0) > invMassWindowB0) {
206-
continue;
207-
}
208-
hMassB0ToDPi->Fill(massDPi);
209-
210-
// calculate relevant properties
211-
const auto& secondaryVertexB0 = df2.getPCACandidate();
212-
auto chi2PCA = df2.getChi2AtPCACandidate();
213-
auto covMatrixPCA = df2.calcPCACovMatrixFlat();
214-
215-
// must be called before getTrack query
216-
df2.propagateTracksToVertex();
217-
// track.getPxPyPzGlo(pVec) modifies pVec of track
218-
df2.getTrack(0).getPxPyPzGlo(pVecD);
219-
df2.getTrack(1).getPxPyPzGlo(pVecPion);
220-
221-
auto primaryVertex = getPrimaryVertex(collision);
222-
auto covMatrixPV = primaryVertex.getCov();
223-
o2::dataformats::DCA impactParameter0;
224-
o2::dataformats::DCA impactParameter1;
225-
trackParCovD.propagateToDCA(primaryVertex, bz, &impactParameter0);
226-
trackParCovPi.propagateToDCA(primaryVertex, bz, &impactParameter1);
227-
228-
hCovSVXX->Fill(covMatrixPCA[0]);
229-
hCovPVXX->Fill(covMatrixPV[0]);
230-
231-
// get uncertainty of the decay length
232-
double phi, theta;
233-
// getPointDirection modifies phi and theta
234-
getPointDirection(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertexB0, phi, theta);
235-
auto errorDecayLength = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta));
236-
auto errorDecayLengthXY = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.));
237-
238-
int hfFlag = BIT(hf_cand_b0::DecayType::B0ToDPi);
239-
240-
// fill the candidate table for the B0 here:
241-
rowCandidateBase(collision.globalIndex(),
242-
collision.posX(), collision.posY(), collision.posZ(),
243-
secondaryVertexB0[0], secondaryVertexB0[1], secondaryVertexB0[2],
244-
errorDecayLength, errorDecayLengthXY,
245-
chi2PCA,
246-
pVecD[0], pVecD[1], pVecD[2],
247-
pVecPion[0], pVecPion[1], pVecPion[2],
248-
impactParameter0.getY(), impactParameter1.getY(),
249-
std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()),
250-
candD.globalIndex(), trackPion.globalIndex(),
251-
hfFlag);
252-
} // pi loop
253-
} // D loop
254-
} // process
255-
}; // struct
241+
const auto& secondaryVertexD = df3.getPCACandidate();
242+
// propagate the 3 prongs to the secondary vertex
243+
trackParCov0.propagateTo(secondaryVertexD[0], bz);
244+
trackParCov1.propagateTo(secondaryVertexD[0], bz);
245+
trackParCov2.propagateTo(secondaryVertexD[0], bz);
246+
247+
// update pVec of tracks
248+
df3.getTrack(0).getPxPyPzGlo(pVec0);
249+
df3.getTrack(1).getPxPyPzGlo(pVec1);
250+
df3.getTrack(2).getPxPyPzGlo(pVec2);
251+
252+
// D∓ → π∓ K± π∓
253+
array<float, 3> pVecPiK = RecoDecay::pVec(pVec0, pVec1);
254+
array<float, 3> pVecD = RecoDecay::pVec(pVec0, pVec1, pVec2);
255+
auto trackParCovPiK = o2::dataformats::V0(df3.getPCACandidatePos(), pVecPiK, df3.calcPCACovMatrixFlat(),
256+
trackParCov0, trackParCov1, {0, 0}, {0, 0});
257+
auto trackParCovD = o2::dataformats::V0(df3.getPCACandidatePos(), pVecD, df3.calcPCACovMatrixFlat(),
258+
trackParCovPiK, trackParCov2, {0, 0}, {0, 0});
259+
260+
int indexTrack0 = track0.globalIndex();
261+
int indexTrack1 = track1.globalIndex();
262+
int indexTrack2 = track2.globalIndex();
263+
264+
auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId);
265+
266+
for (const auto& trackId : trackIdsThisCollision) { // start loop over track indices associated to this collision
267+
auto trackPion = trackId.track_as<TracksWithSel>();
268+
269+
// check isGlobalTrackWoDCA status for pions if wanted
270+
if (usePionIsGlobalTrackWoDCA && !trackPion.isGlobalTrackWoDCA()) {
271+
continue;
272+
}
273+
274+
// minimum pT selection
275+
if (trackPion.pt() < ptPionMin || !isSelectedTrackDCA(trackPion)) {
276+
continue;
277+
}
278+
// reject pions that are D daughters
279+
if (trackPion.globalIndex() == indexTrack0 || trackPion.globalIndex() == indexTrack1 || trackPion.globalIndex() == indexTrack2) {
280+
continue;
281+
}
282+
// reject pi and D with same sign
283+
if (trackPion.sign() * track0.sign() > 0) {
284+
continue;
285+
}
286+
287+
hPtPion->Fill(trackPion.pt());
288+
array<float, 3> pVecPion = {trackPion.px(), trackPion.py(), trackPion.pz()};
289+
auto trackParCovPi = getTrackParCov(trackPion);
290+
291+
// ---------------------------------
292+
// reconstruct the 2-prong B0 vertex
293+
if (df2.process(trackParCovD, trackParCovPi) == 0) {
294+
continue;
295+
}
296+
297+
// calculate relevant properties
298+
const auto& secondaryVertexB0 = df2.getPCACandidate();
299+
auto chi2PCA = df2.getChi2AtPCACandidate();
300+
auto covMatrixPCA = df2.calcPCACovMatrixFlat();
301+
hCovSVXX->Fill(covMatrixPCA[0]);
302+
hCovPVXX->Fill(covMatrixPV[0]);
303+
304+
// propagate D and Pi to the B0 vertex
305+
df2.propagateTracksToVertex();
306+
// track.getPxPyPzGlo(pVec) modifies pVec of track
307+
df2.getTrack(0).getPxPyPzGlo(pVecD); // momentum of D at the B0 vertex
308+
df2.getTrack(1).getPxPyPzGlo(pVecPion); // momentum of Pi at the B0 vertex
309+
310+
// calculate invariant mass and apply selection
311+
massDPi = RecoDecay::m(array{pVecD, pVecPion}, array{massD, massPi});
312+
if (std::abs(massDPi - massB0) > invMassWindowB0) {
313+
continue;
314+
}
315+
hMassB0ToDPi->Fill(massDPi);
316+
317+
// compute impact parameters of D and Pi
318+
o2::dataformats::DCA dcaD;
319+
o2::dataformats::DCA dcaPion;
320+
trackParCovD.propagateToDCA(primaryVertex, bz, &dcaD);
321+
trackParCovPi.propagateToDCA(primaryVertex, bz, &dcaPion);
322+
323+
// get uncertainty of the decay length
324+
double phi, theta;
325+
// getPointDirection modifies phi and theta
326+
getPointDirection(array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertexB0, phi, theta);
327+
auto errorDecayLength = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta));
328+
auto errorDecayLengthXY = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.));
329+
330+
int hfFlag = BIT(hf_cand_b0::DecayType::B0ToDPi);
331+
332+
// fill the candidate table for the B0 here:
333+
rowCandidateBase(thisCollId,
334+
collision.posX(), collision.posY(), collision.posZ(),
335+
secondaryVertexB0[0], secondaryVertexB0[1], secondaryVertexB0[2],
336+
errorDecayLength, errorDecayLengthXY,
337+
chi2PCA,
338+
pVecD[0], pVecD[1], pVecD[2],
339+
pVecPion[0], pVecPion[1], pVecPion[2],
340+
dcaD.getY(), dcaPion.getY(),
341+
std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()),
342+
candD.globalIndex(), trackPion.globalIndex(),
343+
hfFlag);
344+
} // pi loop
345+
} // D loop
346+
} // collision loop
347+
} // process
348+
}; // struct
256349

257350
/// Extends the base table with expression columns and performs MC matching.
258351
struct HfCandidateCreatorB0Expressions {

0 commit comments

Comments
 (0)