Skip to content

Commit 09d2a14

Browse files
committed
Add process for MC closure test
1 parent 1326c2d commit 09d2a14

1 file changed

Lines changed: 215 additions & 3 deletions

File tree

‎PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx‎

Lines changed: 215 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "PWGHF/Core/CentralityEstimation.h"
2020
#include "PWGHF/D2H/Utils/utilsFlow.h"
21+
#include "PWGHF/Core/DecayChannels.h"
2122
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
2223
#include "PWGHF/DataModel/CandidateSelectionTables.h"
2324

@@ -35,6 +36,15 @@
3536
#include <Framework/InitContext.h>
3637
#include <Framework/runDataProcessing.h>
3738

39+
#include <Math/GenVector/Boost.h>
40+
#include <Math/GenVector/RotationZ.h>
41+
#include <Math/Vector3D.h> // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h)
42+
#include <Math/Vector3Dfwd.h>
43+
#include <Math/Vector4D.h> // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h)
44+
#include <Math/Vector4Dfwd.h>
45+
#include <TPDGCode.h>
46+
#include <TRandom3.h>
47+
3848
#include <cstdint>
3949
#include <vector>
4050

@@ -44,6 +54,7 @@ using namespace o2::framework::expressions;
4454
using namespace o2::hf_centrality;
4555
using namespace o2::hf_occupancy;
4656
using namespace o2::analysis::hf_flow_utils;
57+
using namespace o2::constants::physics;
4758

4859
namespace o2::aod
4960
{
@@ -70,6 +81,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float);
7081
DECLARE_SOA_COLUMN(Eta, eta, float);
7182
DECLARE_SOA_COLUMN(Phi, phi, float);
7283
DECLARE_SOA_COLUMN(Y, y, float);
84+
DECLARE_SOA_COLUMN(CosThetaStarEP, cosThetaStarEP, float);
7385
DECLARE_SOA_COLUMN(MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float);
7486
DECLARE_SOA_COLUMN(MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float);
7587
DECLARE_SOA_COLUMN(MlProbDstarToD0PiNonPrompt, mlProbDstarToD0PiNonPrompt, float);
@@ -112,24 +124,45 @@ DECLARE_SOA_TABLE(HfCandDstMix, "AOD", "HFCANDDSTMIX",
112124
mixing_dstar::MinNumTpcCls,
113125
mixing_dstar::GIndexCol,
114126
mixing_dstar::TimeStamp);
127+
DECLARE_SOA_TABLE(HfMcParticles, "AOD", "HFMCPARTICLES",
128+
mixing_dstar::Pt,
129+
mixing_dstar::Eta,
130+
mixing_dstar::Phi,
131+
mixing_dstar::Y,
132+
mixing_dstar::CosThetaStarEP,
133+
mixing_dstar::Centrality);
115134
} // namespace o2::aod
116135

117136
/// Writes the full information in an output TTree
118137
struct HfTreeCreatorDstarSpinAlignMixing {
119138
Produces<o2::aod::HfCandDstMix> rowCandidateMix;
139+
Produces<o2::aod::HfMcParticles> rowGenMcParticles;
120140

121141
Configurable<int> qVecDetector{"qVecDetector", 2, "Detector for Q vector estimation (FV0A: 0, FT0M: 1, FT0C: 2)"};
122142
Configurable<int> centEstimator{"centEstimator", 2, "Centrality estimator ((None: 0, FT0C: 2, FT0M: 3))"};
123143
Configurable<int> occEstimator{"occEstimator", 2, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"};
144+
Configurable<bool> activateAxisRotation{"activateAxisRotation", false, "Activate the rotation of the quantisation axis for cos(Theta*) calculation for phi dependence studies"};
145+
Configurable<float> axisRotationAngle{"axisRotationAngle", 0.f, "Rotation angle of the quantisation axis for cos(Theta*) calculation for phi dependence studies (in rad)"};
146+
Configurable<bool> activatePartRecoDstar{"activatePartRecoDstar", false, "Activate the study of partly reconstructed D*+ -> D0 (-> KPiPi0) Pi decays"};
124147

125148
using CollsWithQVecs = soa::Join<aod::Collisions, aod::EvSels, aod::QvectorFT0Cs, aod::QvectorFT0As, aod::QvectorFT0Ms, aod::QvectorFV0As, aod::QvectorBPoss, aod::QvectorBNegs, aod::QvectorBTots, aod::CentFT0Ms, aod::CentFT0Cs>;
126149
using TracksWithExtra = soa::Join<aod::Tracks, aod::TracksExtra>;
127150
using CandDstarWSelFlag = soa::Join<aod::HfCandDstars, aod::HfSelDstarToD0Pi>;
151+
152+
using CollisionsWithMcLabels = soa::SmallGroups<soa::Join<aod::Collisions, aod::EvSels, aod::PVMults, aod::McCollisionLabels>>;
153+
using CollisionsWithMcLabelsAndCent = soa::SmallGroups<soa::Join<aod::Collisions, aod::EvSels, aod::PVMults, aod::McCollisionLabels, aod::CentFT0Ms, aod::CentFT0Cs>>;
154+
using McParticlesDstarMatched = soa::Join<aod::McParticles, aod::HfCandDstarMcGen>;
155+
using BCsInfo = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels>;
156+
128157
using FilteredCandDstarWSelFlagAndMl = soa::Filtered<soa::Join<CandDstarWSelFlag, aod::HfMlDstarToD0Pi>>;
158+
using FilteredCandDstarWSelFlagAndMcAndMl = soa::Filtered<soa::Join<CandDstarWSelFlag, aod::HfMlDstarToD0Pi, aod::HfCandDstarMcRec>>;
129159

130160
Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true;
131161

132162
Preslice<FilteredCandDstarWSelFlagAndMl> dstarWithMlPerCollision = aod::hf_cand::collisionId;
163+
Preslice<FilteredCandDstarWSelFlagAndMcAndMl> dstarWithMcAndMlPerCollision = aod::hf_cand::collisionId;
164+
165+
PresliceUnsorted<aod::McCollisionLabels> colPerMcCollision = aod::mcparticle::mcCollisionId;
133166

134167
void init(InitContext const&)
135168
{
@@ -159,9 +192,74 @@ struct HfTreeCreatorDstarSpinAlignMixing {
159192
}
160193
}
161194

162-
template <typename CollType, typename T, typename Trk, typename BcType>
195+
/// \param ptCharmHad is the pt of the candidate
196+
double sampleDeltaPhi(float ptCharmHad)
197+
{
198+
199+
double y0 = 6.49e-2;
200+
double cons = 8.42e-1;
201+
double coef = -3.78e-1;
202+
double x0 = -6.37e-1;
203+
double norm = -3.64e-1;
204+
double mu = 4.68e-2;
205+
double sigma = 1.513;
206+
207+
double sigmoid = y0 + (cons - y0) / (1 + std::exp(-coef * (ptCharmHad - x0)));
208+
double peak = norm * std::exp(-(ptCharmHad - mu) * (ptCharmHad - mu) / (2 * sigma * sigma));
209+
210+
double v2 = sigmoid + peak;
211+
const double funcMax = 1.0 + 2.0 * std::abs(v2);
212+
213+
while (true) {
214+
double deltaPhi = gRandom->Uniform(0, constants::math::TwoPI);
215+
double funcVal = gRandom->Uniform(0, funcMax);
216+
217+
double funcDeltaPhi = 1.0 + 2.0 * v2 * std::cos(2.0 * deltaPhi);
218+
219+
if (funcVal < funcDeltaPhi)
220+
return deltaPhi;
221+
}
222+
return -1.;
223+
}
224+
225+
/// \param daughter is the daughter particle
226+
/// \param ptMin is the minimum pt
227+
/// \param etaMax is the maximum eta
228+
/// \return true if daughter is in acceptance
229+
template <typename Part>
230+
bool isDaughterInAcceptance(Part const& daughter, float ptMin, float etaMax)
231+
{
232+
if (daughter.pt() < ptMin) {
233+
return false;
234+
}
235+
if (std::abs(daughter.eta()) > etaMax) {
236+
return false;
237+
}
238+
239+
return true;
240+
}
241+
242+
template <bool DoMc, typename CollType, typename T, typename Trk, typename BcType>
163243
void fillCandidateTable(CollType const& collision, const T& candidate, Trk const& /*tracks*/, BcType const& /*bcs*/)
164244
{
245+
int8_t origin{RecoDecay::OriginType::None};
246+
float ptBhadMother{-1.f};
247+
bool partRecoDstar{false};
248+
if constexpr (DoMc) {
249+
partRecoDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0 && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0;
250+
bool const signalDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK;
251+
if (!signalDstar && (!partRecoDstar || !activatePartRecoDstar)) { // this candidate is not signal and not partially reconstructed signal, skip
252+
return;
253+
}
254+
origin = candidate.originMcRec();
255+
ptBhadMother = candidate.ptBhadMotherPart();
256+
int const pdgBhadMother = candidate.pdgBhadMotherPart();
257+
// For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia.
258+
if (origin == RecoDecay::OriginType::NonPrompt && (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557)) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header)
259+
return;
260+
}
261+
}
262+
165263
const auto bc = collision.template bc_as<BcType>();
166264
const int64_t timeStamp = bc.timestamp();
167265

@@ -186,7 +284,12 @@ struct HfTreeCreatorDstarSpinAlignMixing {
186284
auto trackProng1 = candidate.template prong1_as<Trk>();
187285
auto trackProng2 = candidate.template prongPi_as<Trk>();
188286
getTrackingInfos(std::vector{trackProng0, trackProng1, trackProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin);
189-
std::array<float, 3> const Qvector = getQvec(collision, qVecDetector.value);
287+
std::array<float, 3> Qvector = getQvec(collision, qVecDetector.value);
288+
if constexpr (DoMc) {
289+
double const deltaPhi = sampleDeltaPhi(candidate.pt());
290+
double psi = candidate.phi() - deltaPhi;
291+
Qvector = {static_cast<float>(-std::sin(psi)), static_cast<float>(std::cos(psi)), 0.f};
292+
}
190293

191294
rowCandidateMix(
192295
massD0,
@@ -214,6 +317,91 @@ struct HfTreeCreatorDstarSpinAlignMixing {
214317
timeStamp);
215318
}
216319

320+
/// \param mcParticle is the Mc particle
321+
/// \param mcParticles is the table of Mc particles
322+
/// \param centrality is the centrality of the collision
323+
template <typename Part, typename Particles>
324+
void fillGenParticleTable(Part const& mcParticle, Particles const& mcParticles, float const centrality)
325+
{
326+
int8_t origin{RecoDecay::OriginType::None};
327+
bool partRecoDstar{false};
328+
partRecoDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0);
329+
bool const signalDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK);
330+
331+
if (!signalDstar && (!activatePartRecoDstar || !partRecoDstar)) { // this particle is not signal and not partially reconstructed signal, skip
332+
return;
333+
}
334+
origin = mcParticle.originMcGen();
335+
float massDau{0.f}, massCharmHad{0.f};
336+
if (origin == RecoDecay::OriginType::NonPrompt) {
337+
auto bHadMother = mcParticles.rawIteratorAt(mcParticle.idxBhadMotherPart() - mcParticles.offset());
338+
int const pdgBhadMother = std::abs(bHadMother.pdgCode());
339+
// For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia.
340+
if (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header)
341+
return;
342+
}
343+
}
344+
std::vector<int> listDaughters{};
345+
std::array<int, 2> const dauPdgs = {kPiPlus, o2::constants::physics::Pdg::kD0};
346+
RecoDecay::getDaughters(mcParticle, &listDaughters, dauPdgs, 1);
347+
massDau = MassPiPlus;
348+
massCharmHad = MassDStar;
349+
350+
float const rapidity = mcParticle.y();
351+
if (std::abs(rapidity) > 1.f) { // we do not keep particles with |y| > 1
352+
return;
353+
}
354+
355+
bool areDauInAcc{true};
356+
float pxDau{-1000.f}, pyDau{-1000.f}, pzDau{-1000.f};
357+
for (const auto& dauIdx : listDaughters) {
358+
auto dauPart = mcParticles.rawIteratorAt(dauIdx - mcParticles.offset());
359+
if (std::abs(dauPart.pdgCode()) == kPiPlus) {
360+
pxDau = dauPart.px();
361+
pyDau = dauPart.py();
362+
pzDau = dauPart.pz();
363+
if (areDauInAcc) {
364+
areDauInAcc = isDaughterInAcceptance(dauPart, 0.1, 0.8);
365+
}
366+
} else if (areDauInAcc) { // check also D0 daughters
367+
std::vector<int> listDaughtersD0{};
368+
std::array<int, 2> const dauPdgsD0 = {kPiPlus, -kKPlus};
369+
RecoDecay::getDaughters(mcParticle, &listDaughtersD0, dauPdgsD0, 1);
370+
for (const auto& dauIdxD0 : listDaughtersD0) {
371+
auto dauPartD0 = mcParticles.rawIteratorAt(dauIdxD0 - mcParticles.offset());
372+
areDauInAcc = isDaughterInAcceptance(dauPartD0, 0.3, 0.8);
373+
}
374+
}
375+
}
376+
377+
float const pxCharmHad = mcParticle.px();
378+
float const pyCharmHad = mcParticle.py();
379+
float const pzCharmHad = mcParticle.pz();
380+
ROOT::Math::PxPyPzMVector const fourVecDau = ROOT::Math::PxPyPzMVector(pxDau, pyDau, pzDau, massDau);
381+
ROOT::Math::PxPyPzMVector const fourVecMother = ROOT::Math::PxPyPzMVector(pxCharmHad, pyCharmHad, pzCharmHad, massCharmHad);
382+
ROOT::Math::Boost const boost{fourVecMother.BoostToCM()};
383+
ROOT::Math::PxPyPzMVector const fourVecDauCM = boost(fourVecDau);
384+
ROOT::Math::XYZVector const threeVecDauCM = fourVecDauCM.Vect();
385+
386+
// EP analysis for MC
387+
double const deltaPhi = sampleDeltaPhi(mcParticle.pt());
388+
double psi = mcParticle.phi() - deltaPhi;
389+
ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f);
390+
if (activateAxisRotation) {
391+
qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm);
392+
}
393+
float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2());
394+
395+
rowGenMcParticles(
396+
mcParticle.pt(),
397+
mcParticle.eta(),
398+
mcParticle.phi(),
399+
mcParticle.y(),
400+
cosThetaStarEP,
401+
centrality);
402+
403+
}
404+
217405
void processData(CollsWithQVecs const& collisions,
218406
FilteredCandDstarWSelFlagAndMl const& dstarCandidates,
219407
TracksWithExtra const& tracks,
@@ -223,11 +411,35 @@ struct HfTreeCreatorDstarSpinAlignMixing {
223411
auto thisCollId = collision.globalIndex();
224412
auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMlPerCollision, thisCollId);
225413
for (const auto& dstarCandidate : groupedDstarCandidates) {
226-
fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps);
414+
fillCandidateTable<false>(collision, dstarCandidate, tracks, bcWithTimeStamps);
227415
}
228416
}
229417
}
230418
PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processData, "Process data", true);
419+
420+
void processDstarMcWithMlInPbPb(aod::McCollisions::iterator const&,
421+
McParticlesDstarMatched const& mcParticles,
422+
CollisionsWithMcLabelsAndCent const& collisions, // this is grouped with SmallGroupsCollisionsWithMcLabels const& collisions,
423+
FilteredCandDstarWSelFlagAndMcAndMl const& dstarCandidates,
424+
TracksWithExtra const& tracks,
425+
BCsInfo const& bcWithTimeStamps)
426+
{
427+
for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision
428+
auto thisCollId = collision.globalIndex();
429+
auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMcAndMlPerCollision, thisCollId);
430+
for (const auto& dstarCandidate : groupedDstarCandidates) {
431+
fillCandidateTable<true>(collision, dstarCandidate, tracks, bcWithTimeStamps);
432+
}
433+
}
434+
rowGenMcParticles.reserve(mcParticles.size());
435+
436+
for (const auto& mcParticle : mcParticles) {
437+
const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcParticle.mcCollision().globalIndex());
438+
const auto centrality = getCentralityGenColl(recoCollsPerMcColl, centEstimator);
439+
fillGenParticleTable(mcParticle, mcParticles, centrality);
440+
}
441+
}
442+
PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processDstarMcWithMlInPbPb, "Process Dstar candidates in PbPb MC with ML", false);
231443
};
232444

233445
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)

0 commit comments

Comments
 (0)