Skip to content

Commit 47e1cf9

Browse files
Mingze129alibuild
andauthored
[PWGHF] Dstar spin alignment: Add rotation and extend the MC derived data (#18591)
Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 0927591 commit 47e1cf9

2 files changed

Lines changed: 232 additions & 5 deletions

File tree

‎PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx‎

Lines changed: 215 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
/// \author Mingze li <mingze.li@cern.ch>, CCNU/UniTo
1818

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

2425
#include "Common/Core/RecoDecay.h"
2526
#include "Common/DataModel/Centrality.h"
2627
#include "Common/DataModel/EventSelection.h"
28+
#include "Common/DataModel/Multiplicity.h"
2729
#include "Common/DataModel/Qvectors.h"
2830

31+
#include <CommonConstants/MathConstants.h>
2932
#include <CommonConstants/PhysicsConstants.h>
3033
#include <Framework/ASoA.h>
3134
#include <Framework/AnalysisDataModel.h>
@@ -35,6 +38,16 @@
3538
#include <Framework/InitContext.h>
3639
#include <Framework/runDataProcessing.h>
3740

41+
#include <Math/GenVector/Boost.h>
42+
#include <Math/GenVector/RotationZ.h>
43+
#include <Math/Vector3D.h> // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h)
44+
#include <Math/Vector3Dfwd.h>
45+
#include <Math/Vector4D.h> // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h)
46+
#include <Math/Vector4Dfwd.h>
47+
#include <TPDGCode.h>
48+
#include <TRandom3.h>
49+
50+
#include <cmath>
3851
#include <cstdint>
3952
#include <vector>
4053

@@ -44,6 +57,7 @@ using namespace o2::framework::expressions;
4457
using namespace o2::hf_centrality;
4558
using namespace o2::hf_occupancy;
4659
using namespace o2::analysis::hf_flow_utils;
60+
using namespace o2::constants::physics;
4761

4862
namespace o2::aod
4963
{
@@ -70,6 +84,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float);
7084
DECLARE_SOA_COLUMN(Eta, eta, float);
7185
DECLARE_SOA_COLUMN(Phi, phi, float);
7286
DECLARE_SOA_COLUMN(Y, y, float);
87+
DECLARE_SOA_COLUMN(CosThetaStarEP, cosThetaStarEP, float);
7388
DECLARE_SOA_COLUMN(MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float);
7489
DECLARE_SOA_COLUMN(MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float);
7590
DECLARE_SOA_COLUMN(MlProbDstarToD0PiNonPrompt, mlProbDstarToD0PiNonPrompt, float);
@@ -112,24 +127,45 @@ DECLARE_SOA_TABLE(HfCandDstMix, "AOD", "HFCANDDSTMIX",
112127
mixing_dstar::MinNumTpcCls,
113128
mixing_dstar::GIndexCol,
114129
mixing_dstar::TimeStamp);
130+
DECLARE_SOA_TABLE(HfMcParticles, "AOD", "HFMCPARTICLES",
131+
mixing_dstar::Pt,
132+
mixing_dstar::Eta,
133+
mixing_dstar::Phi,
134+
mixing_dstar::Y,
135+
mixing_dstar::CosThetaStarEP,
136+
mixing_dstar::Centrality);
115137
} // namespace o2::aod
116138

117139
/// Writes the full information in an output TTree
118140
struct HfTreeCreatorDstarSpinAlignMixing {
119141
Produces<o2::aod::HfCandDstMix> rowCandidateMix;
142+
Produces<o2::aod::HfMcParticles> rowGenMcParticles;
120143

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

125151
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>;
126152
using TracksWithExtra = soa::Join<aod::Tracks, aod::TracksExtra>;
127153
using CandDstarWSelFlag = soa::Join<aod::HfCandDstars, aod::HfSelDstarToD0Pi>;
154+
155+
using CollisionsWithMcLabels = soa::SmallGroups<soa::Join<aod::Collisions, aod::EvSels, aod::PVMults, aod::McCollisionLabels>>;
156+
using CollisionsWithMcLabelsAndCent = soa::SmallGroups<soa::Join<aod::Collisions, aod::EvSels, aod::PVMults, aod::McCollisionLabels, aod::CentFT0Ms, aod::CentFT0Cs>>;
157+
using McParticlesDstarMatched = soa::Join<aod::McParticles, aod::HfCandDstarMcGen>;
158+
using BCsInfo = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels>;
159+
128160
using FilteredCandDstarWSelFlagAndMl = soa::Filtered<soa::Join<CandDstarWSelFlag, aod::HfMlDstarToD0Pi>>;
161+
using FilteredCandDstarWSelFlagAndMcAndMl = soa::Filtered<soa::Join<CandDstarWSelFlag, aod::HfMlDstarToD0Pi, aod::HfCandDstarMcRec>>;
129162

130163
Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true;
131164

132165
Preslice<FilteredCandDstarWSelFlagAndMl> dstarWithMlPerCollision = aod::hf_cand::collisionId;
166+
Preslice<FilteredCandDstarWSelFlagAndMcAndMl> dstarWithMcAndMlPerCollision = aod::hf_cand::collisionId;
167+
168+
PresliceUnsorted<aod::McCollisionLabels> colPerMcCollision = aod::mcparticle::mcCollisionId;
133169

134170
void init(InitContext const&)
135171
{
@@ -159,9 +195,72 @@ struct HfTreeCreatorDstarSpinAlignMixing {
159195
}
160196
}
161197

162-
template <typename CollType, typename T, typename Trk, typename BcType>
198+
/// \param ptCharmHad is the pt of the candidate
199+
double sampleDeltaPhi(float ptCharmHad)
200+
{
201+
202+
double y0 = 6.49e-2;
203+
double cons = 8.42e-1;
204+
double coef = -3.78e-1;
205+
double x0 = -6.37e-1;
206+
double norm = -3.64e-1;
207+
double mu = 4.68e-2;
208+
double sigma = 1.513;
209+
210+
double sigmoid = y0 + (cons - y0) / (1 + std::exp(-coef * (ptCharmHad - x0)));
211+
double peak = norm * std::exp(-(ptCharmHad - mu) * (ptCharmHad - mu) / (2 * sigma * sigma));
212+
213+
double v2 = sigmoid + peak;
214+
const double funcMax = 1.0 + 2.0 * std::abs(v2);
215+
216+
while (true) {
217+
double deltaPhi = gRandom->Uniform(0, constants::math::TwoPI);
218+
double funcVal = gRandom->Uniform(0, funcMax);
219+
220+
double funcDeltaPhi = 1.0 + 2.0 * v2 * std::cos(2.0 * deltaPhi);
221+
222+
if (funcVal < funcDeltaPhi)
223+
return deltaPhi;
224+
}
225+
return -1.;
226+
}
227+
228+
/// \param daughter is the daughter particle
229+
/// \param ptMin is the minimum pt
230+
/// \param etaMax is the maximum eta
231+
/// \return true if daughter is in acceptance
232+
template <typename Part>
233+
bool isDaughterInAcceptance(Part const& daughter, float ptMin, float etaMax)
234+
{
235+
if (daughter.pt() < ptMin) {
236+
return false;
237+
}
238+
if (std::abs(daughter.eta()) > etaMax) {
239+
return false;
240+
}
241+
242+
return true;
243+
}
244+
245+
template <bool DoMc, typename CollType, typename T, typename Trk, typename BcType>
163246
void fillCandidateTable(CollType const& collision, const T& candidate, Trk const& /*tracks*/, BcType const& /*bcs*/)
164247
{
248+
int8_t origin{RecoDecay::OriginType::None};
249+
bool partRecoDstar{false};
250+
if constexpr (DoMc) {
251+
partRecoDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0 && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0;
252+
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;
253+
if (!signalDstar && (!partRecoDstar || !activatePartRecoDstar)) { // this candidate is not signal and not partially reconstructed signal, skip
254+
return;
255+
}
256+
origin = candidate.originMcRec();
257+
int const pdgBhadMother = candidate.pdgBhadMotherPart();
258+
// 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.
259+
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)
260+
return;
261+
}
262+
}
263+
165264
const auto bc = collision.template bc_as<BcType>();
166265
const int64_t timeStamp = bc.timestamp();
167266

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

191295
rowCandidateMix(
192296
massD0,
@@ -214,6 +318,90 @@ struct HfTreeCreatorDstarSpinAlignMixing {
214318
timeStamp);
215319
}
216320

321+
/// \param mcParticle is the Mc particle
322+
/// \param mcParticles is the table of Mc particles
323+
/// \param centrality is the centrality of the collision
324+
template <typename Part, typename Particles>
325+
void fillGenParticleTable(Part const& mcParticle, Particles const& mcParticles, float const centrality)
326+
{
327+
int8_t origin{RecoDecay::OriginType::None};
328+
bool partRecoDstar{false};
329+
partRecoDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0);
330+
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);
331+
332+
if (!signalDstar && (!activatePartRecoDstar || !partRecoDstar)) { // this particle is not signal and not partially reconstructed signal, skip
333+
return;
334+
}
335+
origin = mcParticle.originMcGen();
336+
float massDau{0.f}, massCharmHad{0.f};
337+
if (origin == RecoDecay::OriginType::NonPrompt) {
338+
auto bHadMother = mcParticles.rawIteratorAt(mcParticle.idxBhadMotherPart() - mcParticles.offset());
339+
int const pdgBhadMother = std::abs(bHadMother.pdgCode());
340+
// 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.
341+
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)
342+
return;
343+
}
344+
}
345+
std::vector<int> listDaughters{};
346+
std::array<int, 2> const dauPdgs = {kPiPlus, o2::constants::physics::Pdg::kD0};
347+
RecoDecay::getDaughters(mcParticle, &listDaughters, dauPdgs, 1);
348+
massDau = MassPiPlus;
349+
massCharmHad = MassDStar;
350+
351+
float const rapidity = mcParticle.y();
352+
if (std::abs(rapidity) > 1.f) { // we do not keep particles with |y| > 1
353+
return;
354+
}
355+
356+
bool areDauInAcc{true};
357+
float pxDau{-1000.f}, pyDau{-1000.f}, pzDau{-1000.f};
358+
for (const auto& dauIdx : listDaughters) {
359+
auto dauPart = mcParticles.rawIteratorAt(dauIdx - mcParticles.offset());
360+
if (std::abs(dauPart.pdgCode()) == kPiPlus) {
361+
pxDau = dauPart.px();
362+
pyDau = dauPart.py();
363+
pzDau = dauPart.pz();
364+
if (areDauInAcc) {
365+
areDauInAcc = isDaughterInAcceptance(dauPart, 0.1, 0.8);
366+
}
367+
} else if (areDauInAcc) { // check also D0 daughters
368+
std::vector<int> listDaughtersD0{};
369+
std::array<int, 2> const dauPdgsD0 = {kPiPlus, -kKPlus};
370+
RecoDecay::getDaughters(mcParticle, &listDaughtersD0, dauPdgsD0, 1);
371+
for (const auto& dauIdxD0 : listDaughtersD0) {
372+
auto dauPartD0 = mcParticles.rawIteratorAt(dauIdxD0 - mcParticles.offset());
373+
areDauInAcc = isDaughterInAcceptance(dauPartD0, 0.3, 0.8);
374+
}
375+
}
376+
}
377+
378+
float const pxCharmHad = mcParticle.px();
379+
float const pyCharmHad = mcParticle.py();
380+
float const pzCharmHad = mcParticle.pz();
381+
ROOT::Math::PxPyPzMVector const fourVecDau = ROOT::Math::PxPyPzMVector(pxDau, pyDau, pzDau, massDau);
382+
ROOT::Math::PxPyPzMVector const fourVecMother = ROOT::Math::PxPyPzMVector(pxCharmHad, pyCharmHad, pzCharmHad, massCharmHad);
383+
ROOT::Math::Boost const boost{fourVecMother.BoostToCM()};
384+
ROOT::Math::PxPyPzMVector const fourVecDauCM = boost(fourVecDau);
385+
ROOT::Math::XYZVector const threeVecDauCM = fourVecDauCM.Vect();
386+
387+
// EP analysis for MC
388+
double const deltaPhi = sampleDeltaPhi(mcParticle.pt());
389+
double psi = mcParticle.phi() - deltaPhi;
390+
ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f);
391+
if (activateAxisRotation) {
392+
qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm);
393+
}
394+
float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2());
395+
396+
rowGenMcParticles(
397+
mcParticle.pt(),
398+
mcParticle.eta(),
399+
mcParticle.phi(),
400+
mcParticle.y(),
401+
cosThetaStarEP,
402+
centrality);
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)