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>
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;
4457using namespace o2 ::hf_centrality;
4558using namespace o2 ::hf_occupancy;
4659using namespace o2 ::analysis::hf_flow_utils;
60+ using namespace o2 ::constants::physics;
4761
4862namespace o2 ::aod
4963{
@@ -70,6 +84,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float);
7084DECLARE_SOA_COLUMN (Eta, eta, float );
7185DECLARE_SOA_COLUMN (Phi, phi, float );
7286DECLARE_SOA_COLUMN (Y, y, float );
87+ DECLARE_SOA_COLUMN (CosThetaStarEP, cosThetaStarEP, float );
7388DECLARE_SOA_COLUMN (MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float );
7489DECLARE_SOA_COLUMN (MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float );
7590DECLARE_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
118140struct 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
233445WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
0 commit comments