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
2729using namespace o2 ;
2830using 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.
258351struct HfCandidateCreatorB0Expressions {
0 commit comments