Skip to content

Commit f637054

Browse files
dsekihatalibuild
andauthored
[PWGEM/Dilepton] add a task to produce ref tracks with MFTsa for flow (#14702)
Co-authored-by: ALICE Action Bot <[email protected]>
1 parent 837ca24 commit f637054

File tree

3 files changed

+360
-0
lines changed

3 files changed

+360
-0
lines changed

PWGEM/Dilepton/TableProducer/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ o2physics_add_dpl_workflow(skimmer-primary-track
4141
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore
4242
COMPONENT_NAME Analysis)
4343

44+
o2physics_add_dpl_workflow(skimmer-primary-mfttrack
45+
SOURCES skimmerPrimaryMFTTrack.cxx
46+
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking
47+
COMPONENT_NAME Analysis)
48+
4449
o2physics_add_dpl_workflow(create-emevent-dilepton
4550
SOURCES createEMEventDilepton.cxx
4651
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \brief write relevant information about MFTsa tracks for reference flow.
13+
/// \author [email protected]
14+
15+
#include "PWGEM/Dilepton/DataModel/dileptonTables.h"
16+
#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h"
17+
18+
#include "Common/Core/TableHelper.h"
19+
#include "Common/Core/fwdtrackUtilities.h"
20+
// #include "Common/DataModel/CollisionAssociationTables.h"
21+
22+
#include "CCDB/BasicCCDBManager.h"
23+
#include "CommonConstants/PhysicsConstants.h"
24+
#include "DataFormatsParameters/GRPMagField.h"
25+
#include "DataFormatsParameters/GRPObject.h"
26+
#include "DetectorsBase/GeometryManager.h"
27+
#include "DetectorsBase/Propagator.h"
28+
#include "Framework/AnalysisDataModel.h"
29+
#include "Framework/AnalysisTask.h"
30+
#include "Framework/runDataProcessing.h"
31+
#include "GlobalTracking/MatchGlobalFwd.h"
32+
#include "MCHTracking/TrackExtrap.h"
33+
#include "MCHTracking/TrackParam.h"
34+
#include "ReconstructionDataFormats/TrackFwd.h"
35+
36+
#include <map>
37+
#include <string>
38+
#include <utility>
39+
#include <vector>
40+
41+
using namespace o2;
42+
using namespace o2::soa;
43+
using namespace o2::framework;
44+
using namespace o2::framework::expressions;
45+
using namespace o2::constants::physics;
46+
using namespace o2::aod::fwdtrackutils;
47+
using namespace o2::aod::pwgem::dilepton::utils::emtrackutil;
48+
49+
struct skimmerPrimaryMFTTrack {
50+
using MyCollisions = soa::Join<aod::Collisions, aod::EvSels, aod::EMEvSels, aod::EMEoIs>;
51+
using MyCollisionsWithSWT = soa::Join<MyCollisions, aod::EMSWTriggerBitsTMP>;
52+
using MyCollisionsMC = soa::Join<MyCollisions, aod::McCollisionLabels>;
53+
54+
using MyTracks = aod::MFTTracks;
55+
using MyTrack = MyTracks::iterator;
56+
using MyTracksMC = soa::Join<aod::MFTTracks, aod::McMFTTrackLabels>;
57+
using MyTrackMC = MyTracksMC::iterator;
58+
59+
SliceCache cache;
60+
Preslice<aod::MFTTracks> perCol = o2::aod::fwdtrack::collisionId;
61+
Produces<aod::EMPrimaryTracks> emprimarytracks;
62+
63+
// Configurables
64+
Configurable<std::string> ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
65+
Configurable<std::string> grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"};
66+
Configurable<std::string> geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"};
67+
68+
Configurable<bool> fillQAHistogram{"fillQAHistogram", true, "flag to fill QA histograms"};
69+
70+
Configurable<float> cfgPtMin{"cfgPtMin", 0.2, "min pt for MFTsa track"};
71+
Configurable<float> cfgPtMax{"cfgPtMax", 1e+10, "max pt for MFTsa track"};
72+
Configurable<float> cfgEtaMin{"cfgEtaMin", -4, "min eta acceptance"};
73+
Configurable<float> cfgEtaMax{"cfgEtaMax", -2, "max eta acceptance"};
74+
75+
// for z shift for propagation
76+
Configurable<bool> cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift from CCDB"};
77+
Configurable<std::string> cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"};
78+
Configurable<float> cfgManualZShift{"cfgManualZShift", 0, "manual z-shift for propagation of global muon to PV"};
79+
80+
HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false};
81+
82+
int mRunNumber = 0;
83+
float mBz = 0;
84+
float mZShift = 0;
85+
Service<o2::ccdb::BasicCCDBManager> ccdb;
86+
o2::ccdb::CcdbApi ccdbApi;
87+
88+
void init(InitContext&)
89+
{
90+
mRunNumber = 0;
91+
mBz = 0;
92+
mZShift = 0;
93+
94+
ccdb->setURL(ccdburl);
95+
ccdb->setCaching(true);
96+
ccdb->setLocalObjectValidityChecking();
97+
ccdb->setFatalWhenNull(false);
98+
ccdbApi.init(ccdburl);
99+
100+
if (fillQAHistogram) {
101+
fRegistry.add("MFT/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{100, 0.0f, 10}}, false);
102+
fRegistry.add("MFT/hPtEta", "pT vs. #eta;#eta;p_{T} (GeV/c)", kTH2F, {{200, -4.f, -2.f}, {100, 0.0f, 10}}, false);
103+
fRegistry.add("MFT/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{2000, -10, 10}}, false);
104+
fRegistry.add("MFT/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {200, -4.0f, -2.0f}}, false);
105+
fRegistry.add("MFT/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", kTH2F, {{200, -0.1f, 0.1f}, {200, -0.1f, 0.1f}}, false);
106+
fRegistry.add("MFT/hDCAxy", "DCA xy;DCA_{xy} (cm)", kTH1F, {{100, 0, 0.1f}}, false);
107+
fRegistry.add("MFT/hDCAz", "DCA z;DCA_{z} (cm)", kTH1F, {{100, -0.05f, 0.05f}}, false);
108+
fRegistry.add("MFT/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{100, 0, 0.1f}, {100, -0.05f, 0.05f}}, false);
109+
fRegistry.add("MFT/hNclsMFT", "number of MFT clusters", kTH1F, {{11, -0.5, 10.5}}, false);
110+
fRegistry.add("MFT/hChi2MFT", "chi2/ndf;chi2/ndf", kTH1F, {{100, 0, 10}}, false);
111+
fRegistry.add("MFT/hDCAx_PosZ", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {200, -0.1, +0.1}}, false);
112+
fRegistry.add("MFT/hDCAy_PosZ", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {200, -0.1, +0.1}}, false);
113+
fRegistry.add("MFT/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", kTH2F, {{180, 0, 2 * M_PI}, {200, -0.1, +0.1}}, false);
114+
fRegistry.add("MFT/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", kTH2F, {{180, 0, 2 * M_PI}, {200, -0.1, +0.1}}, false);
115+
}
116+
}
117+
118+
void initCCDB(aod::BCsWithTimestamps::iterator const& bc)
119+
{
120+
if (mRunNumber == bc.runNumber()) {
121+
return;
122+
}
123+
mRunNumber = bc.runNumber();
124+
LOGF(info, "mRunNumber = %d", mRunNumber);
125+
std::map<std::string, std::string> metadata;
126+
auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, mRunNumber);
127+
auto ts = soreor.first;
128+
auto grpmag = ccdbApi.retrieveFromTFileAny<o2::parameters::GRPMagField>(grpmagPath, metadata, ts);
129+
o2::base::Propagator::initFieldFromGRP(grpmag);
130+
if (!o2::base::GeometryManager::isGeometryLoaded()) {
131+
ccdb->get<TGeoManager>(geoPath);
132+
}
133+
o2::mch::TrackExtrap::setField();
134+
const double centerMFT[3] = {0, 0, -61.4};
135+
o2::field::MagneticField* field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField());
136+
mBz = field->getBz(centerMFT); // Get field at centre of MFT
137+
LOGF(info, "Bz at center of MFT = %f kZG", mBz);
138+
139+
if (cfgApplyZShiftFromCCDB) {
140+
auto* zShift = ccdb->getForTimeStamp<std::vector<float>>(cfgZShiftPath, bc.timestamp());
141+
if (zShift != nullptr && !zShift->empty()) {
142+
LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value);
143+
mZShift = (*zShift)[0];
144+
} else {
145+
LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value);
146+
mZShift = 0;
147+
}
148+
} else {
149+
LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value);
150+
mZShift = cfgManualZShift;
151+
}
152+
}
153+
154+
template <bool isMC, typename TCollision, typename TTrack>
155+
bool checkTrack(TCollision const&, TTrack const& track)
156+
{
157+
if constexpr (isMC) {
158+
if (!track.has_mcParticle()) {
159+
return false;
160+
}
161+
}
162+
163+
return true;
164+
}
165+
166+
template <typename TCollision, typename TTrack>
167+
void fillTrackTable(TCollision const& collision, TTrack const& mfttrack)
168+
{
169+
// propagate MFTsa track to PV
170+
std::array<double, 3> dcaInfOrig{999.f, 999.f, 999.f};
171+
std::vector<double> v1; // Temporary null vector for the computation of the covariance matrix
172+
SMatrix55 tcovs(v1.begin(), v1.end());
173+
SMatrix5 tpars(mfttrack.x(), mfttrack.y(), mfttrack.phi(), mfttrack.tgl(), mfttrack.signed1Pt());
174+
o2::track::TrackParCovFwd trackPar{mfttrack.z() + mZShift, tpars, tcovs, mfttrack.chi2()};
175+
trackPar.propagateToDCAhelix(mBz, {collision.posX(), collision.posY(), collision.posZ()}, dcaInfOrig);
176+
v1.clear();
177+
v1.shrink_to_fit();
178+
float dcaX = dcaInfOrig[0];
179+
float dcaY = dcaInfOrig[1];
180+
float dcaZ = dcaInfOrig[2];
181+
float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY);
182+
183+
float pt = trackPar.getPt();
184+
float eta = trackPar.getEta();
185+
float phi = trackPar.getPhi();
186+
o2::math_utils::bringTo02Pi(phi);
187+
188+
if (pt < cfgPtMin || cfgPtMax < pt) {
189+
return;
190+
}
191+
if (eta < cfgEtaMin || cfgEtaMax < eta) {
192+
return;
193+
}
194+
195+
uint16_t trackBit = 0;
196+
int ndf = 2 * mfttrack.nClusters() - 5;
197+
198+
// As minimal cuts, following cuts are applied. The cut values are hardcoded on the purpose for consistent bit operation.
199+
// Ncls MFT >= 5
200+
// chi2/ndf MFT < 5
201+
// |dcaXY| < 0.06 cm
202+
203+
if (mfttrack.nClusters() < 6 || mfttrack.chi2() / ndf > 5.f || std::fabs(dcaXY) > 0.05) {
204+
return;
205+
}
206+
207+
if (mfttrack.nClusters() >= 7) {
208+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kNclsMFT7);
209+
}
210+
if (mfttrack.nClusters() >= 8) {
211+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kNclsMFT8);
212+
}
213+
214+
if (mfttrack.chi2() / ndf < 4.f) {
215+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kChi2MFT4);
216+
}
217+
if (mfttrack.chi2() / ndf < 3.f) {
218+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kChi2MFT3);
219+
}
220+
221+
if (std::fabs(dcaXY) < 0.04) {
222+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kDCAxy004cm);
223+
}
224+
if (std::fabs(dcaXY) < 0.03) {
225+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kDCAxy003cm);
226+
}
227+
if (std::fabs(dcaXY) < 0.02) {
228+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kDCAxy002cm);
229+
}
230+
if (std::fabs(dcaXY) < 0.01) {
231+
trackBit |= static_cast<uint16_t>(RefMFTTrackBit::kDCAxy001cm);
232+
}
233+
234+
emprimarytracks(collision.globalIndex(), mfttrack.globalIndex(), mfttrack.sign() / pt, eta, phi, trackBit);
235+
236+
if (fillQAHistogram) {
237+
fRegistry.fill(HIST("MFT/hPt"), pt);
238+
fRegistry.fill(HIST("MFT/hPtEta"), eta, pt);
239+
fRegistry.fill(HIST("MFT/hQoverPt"), mfttrack.sign() / pt);
240+
fRegistry.fill(HIST("MFT/hEtaPhi"), phi, eta);
241+
fRegistry.fill(HIST("MFT/hDCAxy2D"), dcaX, dcaY);
242+
fRegistry.fill(HIST("MFT/hDCAxy"), dcaXY);
243+
fRegistry.fill(HIST("MFT/hDCAz"), dcaZ);
244+
fRegistry.fill(HIST("MFT/hDCAxyz"), dcaXY, dcaZ);
245+
fRegistry.fill(HIST("MFT/hNclsMFT"), mfttrack.nClusters());
246+
fRegistry.fill(HIST("MFT/hChi2MFT"), mfttrack.chi2() / ndf);
247+
fRegistry.fill(HIST("MFT/hDCAx_PosZ"), collision.posZ(), dcaX);
248+
fRegistry.fill(HIST("MFT/hDCAy_PosZ"), collision.posZ(), dcaY);
249+
fRegistry.fill(HIST("MFT/hDCAx_Phi"), phi, dcaX);
250+
fRegistry.fill(HIST("MFT/hDCAy_Phi"), phi, dcaY);
251+
}
252+
}
253+
254+
// ---------- for data ----------
255+
256+
void processRec(MyCollisions const& collisions, aod::BCsWithTimestamps const&, MyTracks const& tracks)
257+
{
258+
259+
for (const auto& collision : collisions) {
260+
auto bc = collision.template foundBC_as<aod::BCsWithTimestamps>();
261+
initCCDB(bc);
262+
if (!collision.isSelected()) {
263+
continue;
264+
}
265+
if (!collision.isEoI()) { // events with at least 1 lepton for data reduction.
266+
continue;
267+
}
268+
269+
auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex());
270+
for (const auto& track : tracks_per_coll) {
271+
if (!checkTrack<false>(collision, track)) {
272+
continue;
273+
}
274+
fillTrackTable(collision, track);
275+
}
276+
277+
} // end of collision loop
278+
}
279+
PROCESS_SWITCH(skimmerPrimaryMFTTrack, processRec, "process reconstructed info only", true); // standalone
280+
281+
void processRec_SWT(MyCollisionsWithSWT const& collisions, aod::BCsWithTimestamps const&, MyTracks const& tracks)
282+
{
283+
284+
for (const auto& collision : collisions) {
285+
auto bc = collision.template foundBC_as<aod::BCsWithTimestamps>();
286+
initCCDB(bc);
287+
288+
if (!collision.isSelected()) {
289+
continue;
290+
}
291+
if (!collision.isEoI()) {
292+
continue;
293+
}
294+
if (collision.swtaliastmp_raw() == 0) {
295+
continue;
296+
}
297+
298+
auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex());
299+
for (const auto& track : tracks_per_coll) {
300+
if (!checkTrack<false>(collision, track)) {
301+
continue;
302+
}
303+
fillTrackTable(collision, track);
304+
}
305+
306+
} // end of collision loop
307+
}
308+
PROCESS_SWITCH(skimmerPrimaryMFTTrack, processRec_SWT, "process reconstructed info only", false); // standalone with swt
309+
310+
// ---------- for MC ----------
311+
312+
void processMC(MyCollisionsMC const& collisions, aod::McCollisions const&, aod::BCsWithTimestamps const&, MyTracksMC const& tracks)
313+
{
314+
315+
for (const auto& collision : collisions) {
316+
if (!collision.has_mcCollision()) {
317+
continue;
318+
}
319+
auto bc = collision.template foundBC_as<aod::BCsWithTimestamps>();
320+
initCCDB(bc);
321+
322+
if (!collision.isSelected()) {
323+
continue;
324+
}
325+
if (!collision.isEoI()) { // events with at least 1 lepton for data reduction.
326+
continue;
327+
}
328+
329+
auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex());
330+
for (const auto& track : tracks_per_coll) {
331+
if (!checkTrack<true>(collision, track)) {
332+
continue;
333+
}
334+
fillTrackTable(collision, track);
335+
}
336+
} // end of collision loop
337+
}
338+
PROCESS_SWITCH(skimmerPrimaryMFTTrack, processMC, "process reconstructed and MC info ", false);
339+
};
340+
341+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
342+
{
343+
return WorkflowSpec{adaptAnalysisTask<skimmerPrimaryMFTTrack>(cfgc, TaskName{"skimmer-primary-mfttrack"})};
344+
}

PWGEM/Dilepton/Utils/EMTrackUtilities.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ enum class RefTrackBit : uint16_t { // This is not for leptons, but charged trac
4444
kDCAz03cm = 8192,
4545
};
4646

47+
enum class RefMFTTrackBit : uint16_t { // This is not for leptons, but charged tracks for reference flow.
48+
kNclsMFT7 = 1, // default is 6
49+
kNclsMFT8 = 2,
50+
kChi2MFT4 = 4, // default is 5
51+
kChi2MFT3 = 8,
52+
kDCAxy004cm = 16, // default is 0.05 cm
53+
kDCAxy003cm = 32,
54+
kDCAxy002cm = 64,
55+
kDCAxy001cm = 128,
56+
};
57+
4758
//_______________________________________________________________________
4859
template <typename T>
4960
float dca3DinSigma(T const& track)

0 commit comments

Comments
 (0)