2525#include " Common/Core/RecoDecay.h"
2626
2727#include < CommonConstants/MathConstants.h>
28+ #include < CommonConstants/PhysicsConstants.h>
2829#include < Framework/ASoA.h>
2930#include < Framework/AnalysisDataModel.h>
3031#include < Framework/AnalysisHelpers.h>
@@ -69,6 +70,7 @@ using namespace o2;
6970using namespace o2 ::aod;
7071using namespace o2 ::framework;
7172using namespace o2 ::framework::expressions;
73+ using namespace o2 ::constants::physics;
7274using emcClusters = o2::soa::Join<o2::aod::JClusters, o2::aod::JClusterTracks>;
7375using emcMCClusters = o2::soa::Join<o2::aod::JMcClusterLbs, o2::aod::JClusters, o2::aod::JClusterTracks>;
7476
@@ -85,6 +87,7 @@ struct GammaJetTreeProducer {
8587 Produces<aod::GjChJetMCInfos> chJetMCInfosTable; // detector level charged jets MC information
8688 Produces<aod::GjMCJets> mcJetsTable; // gen level jets
8789 Produces<aod::GjJetSubstructures> jetSubstructuresTable; // jet substructure observables
90+ Produces<aod::GjMCJetSubstructures> mcJetSubstructuresTable; // MC gen-level jet substructure observables
8891
8992 HistogramRegistry mHistograms {" GammaJetTreeProducerHisto" };
9093
@@ -95,7 +98,7 @@ struct GammaJetTreeProducer {
9598 // ---------------
9699
97100 // event cuts
98- Configurable<double > mVertexCut {" vertexCut" , 10.0 , " apply z-vertex cut with value in cm" };
101+ Configurable<double > vertexCut {" vertexCut" , 10.0 , " apply z-vertex cut with value in cm" };
99102 Configurable<std::string> eventSelections{" eventSelections" , " sel8" , " choose event selection" };
100103 Configurable<std::string> triggerMasks{" triggerMasks" , " " , " possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL" };
101104 Configurable<std::string> trackSelections{" trackSelections" , " globalTracks" , " set track selections" };
@@ -139,6 +142,9 @@ struct GammaJetTreeProducer {
139142 std::vector<float > ptSubLeadingVec;
140143 std::vector<float > thetaVec;
141144
145+ // other constants
146+ const float kInvalidValue = -99 .0f ;
147+
142148 // keeping track of the current collision index
143149 // to determine if we need to rebuild the kdTree
144150 // this makes loadorder more robust
@@ -397,7 +403,7 @@ struct GammaJetTreeProducer {
397403 {
398404 mHistograms .fill (HIST (" eventQA" ), 0 );
399405
400- if (std::abs (collision.posZ ()) > mVertexCut ) {
406+ if (std::abs (collision.posZ ()) > vertexCut ) {
401407 return false ;
402408 }
403409 mHistograms .fill (HIST (" eventQA" ), 1 );
@@ -566,8 +572,7 @@ struct GammaJetTreeProducer {
566572 {
567573 T current = particle;
568574 int depth = 0 ;
569- bool continueTracing = true ;
570- while (continueTracing) {
575+ while (depth < kMaxRecursionDepth ) {
571576 LOG (info) << " Level " << depth
572577 << " | PDG: " << current.pdgCode ()
573578 << " | E: " << current.energy ()
@@ -719,7 +724,7 @@ struct GammaJetTreeProducer {
719724 // check if it has mothers that are etas
720725 const auto & mothers = particle.template mothers_as <aod::JMcParticles>();
721726 for (const auto & mother : mothers) {
722- if (mother.pdgCode () == 221 ) {
727+ if (mother.pdgCode () == Pdg:: kEta ) {
723728 return true ;
724729 }
725730 }
@@ -736,7 +741,7 @@ struct GammaJetTreeProducer {
736741 // check if you find a pi0 mother or a eta mother
737742 const auto & mothers = particle.template mothers_as <aod::JMcParticles>();
738743 for (const auto & mother : mothers) {
739- if (mother.pdgCode () == PDG_t::kPi0 || mother.pdgCode () == 221 ) {
744+ if (mother.pdgCode () == PDG_t::kPi0 || mother.pdgCode () == Pdg:: kEta ) {
740745 return false ;
741746 }
742747 }
@@ -927,15 +932,16 @@ struct GammaJetTreeProducer {
927932 // / \brief Gets the origin bitmap for a cluster
928933 // / \param cluster The cluster to check
929934 // / \param mcParticles The MC particles collection
930- // / \return A bitmap indicating the cluster's origin
935+ // / \return A a pari containing the bitmap of the origin of the particle, as well as the MC index of the associated MC gen particle
931936 template <typename T, typename U>
932- uint16_t getClusterOrigin (const T& cluster, U const & mcParticles)
937+ std::pair< uint16_t , int > getClusterOrigin (const T& cluster, U const & mcParticles)
933938 {
934939 uint16_t origin = 0 ;
940+ int returnMCIndex = -1 ;
935941 auto inducerIDs = cluster.mcParticlesIds ();
936942 if (inducerIDs.size () == 0 ) {
937943 SETBIT (origin, static_cast <uint16_t >(gjanalysis::ClusterOrigin::kUnknown ));
938- return origin;
944+ return std::make_pair ( origin, returnMCIndex) ;
939945 }
940946
941947 // loop over all inducers and print their energy
@@ -960,7 +966,7 @@ struct GammaJetTreeProducer {
960966 LOG (debug) << " Leading particle primary ID: " << leadingParticlePrimaryID;
961967 if (leadingParticlePrimaryID == -1 ) {
962968 SETBIT (origin, static_cast <uint16_t >(gjanalysis::ClusterOrigin::kUnknown ));
963- return origin;
969+ return std::make_pair ( origin, returnMCIndex) ;
964970 }
965971 const auto & leadingParticlePrimary = mcParticles.iteratorAt (leadingParticlePrimaryID);
966972 LOG (debug) << " Leading particle primary PDG: " << leadingParticlePrimary.pdgCode ();
@@ -993,17 +999,25 @@ struct GammaJetTreeProducer {
993999 LOG (debug) << " Leading particle primary is a decay photon from eta" ;
9941000 }
9951001
1002+ // for all the cases so far, we can use as MC index the index of the leading particle primary
1003+ returnMCIndex = leadingParticlePrimaryID;
1004+
9961005 // Do checks if a cluster is a merged pi0 decay
9971006 // we classify a cluster as merged pi0 if the leading and subleading contribution to a cluster come from two photons that are part of a pi0 decay
9981007 if (isMergedFromPDGDecay (cluster, mcParticles, PDG_t::kPi0 )) {
9991008 SETBIT (origin, static_cast <uint16_t >(gjanalysis::ClusterOrigin::kMergedPi0 ));
10001009 LOG (debug) << " Cluster is a merged pi0" ;
1010+ // if this is a merged pion decay, it should return the MC index of the pi0 mother (it is ensured that this is properly returned otherwise we would not be in this if statement)
1011+ returnMCIndex = getIndexMotherChain (mcParticles.iteratorAt (inducerIDs[0 ]), mcParticles, PDG_t::kPi0 );
1012+
10011013 }
1002- if (isMergedFromPDGDecay (cluster, mcParticles, 221 )) {
1014+ if (isMergedFromPDGDecay (cluster, mcParticles, Pdg:: kEta )) {
10031015 SETBIT (origin, static_cast <uint16_t >(gjanalysis::ClusterOrigin::kMergedEta ));
10041016 LOG (debug) << " Cluster is a merged eta" ;
1017+ returnMCIndex = getIndexMotherChain (mcParticles.iteratorAt (inducerIDs[0 ]), mcParticles, Pdg::kEta );
10051018 }
10061019
1020+
10071021 // check if photon conversion
10081022 // check that leading contribution is an electron or positron
10091023 LOG (debug) << " Checking if cluster is a converted photon" ;
@@ -1023,6 +1037,8 @@ struct GammaJetTreeProducer {
10231037 if ((daughters.iteratorAt (0 ).pdgCode () == PDG_t::kElectron && daughters.iteratorAt (1 ).pdgCode () == PDG_t::kPositron ) || (daughters.iteratorAt (0 ).pdgCode () == PDG_t::kPositron && daughters.iteratorAt (1 ).pdgCode () == PDG_t::kElectron )) {
10241038 SETBIT (origin, static_cast <uint16_t >(gjanalysis::ClusterOrigin::kConvertedPhoton ));
10251039 LOG (debug) << " Cluster is a converted photon" ;
1040+ // if we found a converted photon, it should use for the the MC index the one of the photon
1041+ returnMCIndex = mother.globalIndex ();
10261042 }
10271043 }
10281044 }
@@ -1031,7 +1047,7 @@ struct GammaJetTreeProducer {
10311047 }
10321048 // display bit origin
10331049 LOG (debug) << " Origin bits: " << std::bitset<16 >(origin);
1034- return origin;
1050+ return std::make_pair ( origin, returnMCIndex) ;
10351051 }
10361052
10371053 // ---------------------
@@ -1046,7 +1062,6 @@ struct GammaJetTreeProducer {
10461062 mcCollisionsMultiRecCollisions.clear ();
10471063 mcJetIndexMapping.clear ();
10481064 }
1049- PROCESS_SWITCH (GammaJetTreeProducer, processClearMaps, " process function that clears all the maps in each dataframe" , true );
10501065
10511066 // WARNING: This function always has to run second in the processing chain
10521067 // / \brief Processes MC event matching QA
@@ -1063,7 +1078,7 @@ struct GammaJetTreeProducer {
10631078 int nRecCollisions = 0 ;
10641079 mHistograms .fill (HIST (" mcCollisionsWithRecCollisions" ), 0 );
10651080 for (auto const & collision : collisions) {
1066- if (std::abs (collision.posZ ()) > mVertexCut ) {
1081+ if (std::abs (collision.posZ ()) > vertexCut ) {
10671082 continue ;
10681083 }
10691084 if (!jetderiveddatautilities::selectCollision (collision, eventSelectionBits, true , true , rctLabel)) {
@@ -1099,7 +1114,6 @@ struct GammaJetTreeProducer {
10991114 mHistograms .fill (HIST (" numberRecCollisionsVsPhotonPt" ), nRecCollisions, particle.pt ());
11001115 }
11011116 }
1102- PROCESS_SWITCH (GammaJetTreeProducer, processMCCollisionsMatching, " Process MC event matching QA" , false );
11031117
11041118 // / \brief Processes data events in data fill event table
11051119 // / \param collision The collision to process
@@ -1113,7 +1127,6 @@ struct GammaJetTreeProducer {
11131127 eventsTable (collision.multFT0M (), collision.centFT0M (), collision.rho (), collision.eventSel (), collision.trackOccupancyInTimeRange (), collision.alias_raw ());
11141128 collisionMapping[collision.globalIndex ()] = eventsTable.lastIndex ();
11151129 }
1116- PROCESS_SWITCH (GammaJetTreeProducer, processEventData, " Process event data" , true );
11171130
11181131 using MCCol = o2::soa::Join<aod::JMcCollisions, aod::BkgChargedMcRhos>;
11191132
@@ -1154,7 +1167,6 @@ struct GammaJetTreeProducer {
11541167 }
11551168 mcEventsTable (eventsTable.lastIndex (), mcCollision.weight (), mcCollision.rho (), isMultipleAssigned);
11561169 }
1157- PROCESS_SWITCH (GammaJetTreeProducer, processEventMC, " Process MC event MC" , false );
11581170
11591171 // ---------------------
11601172 // Processing functions can be safely added below this line
@@ -1224,8 +1236,6 @@ struct GammaJetTreeProducer {
12241236 mHistograms .fill (HIST (" trackPt" ), track.pt ());
12251237 }
12261238 }
1227- PROCESS_SWITCH (GammaJetTreeProducer, processClusters, " Process EMCal clusters" , true );
1228-
12291239 // / \brief Processes MC cluster information (rec level)
12301240 // / \param collision The collision to process
12311241 // / \param mcClusters The MC clusters to process
@@ -1240,7 +1250,7 @@ struct GammaJetTreeProducer {
12401250 // TODO: add weights
12411251 for (const auto & mcCluster : mcClusters) {
12421252 mHistograms .fill (HIST (" clusterMC_E_All" ), mcCluster.energy ());
1243- uint16_t origin = getClusterOrigin (mcCluster, mcParticles);
1253+ auto [ origin, mcIndex] = getClusterOrigin (mcCluster, mcParticles);
12441254 float leadingEnergyFraction = mcCluster.amplitudeA ()[0 ] / mcCluster.energy ();
12451255 // Fill MC origin QA histograms
12461256 if (origin & (1 << static_cast <uint16_t >(gjanalysis::ClusterOrigin::kPhoton ))) {
@@ -1283,11 +1293,26 @@ struct GammaJetTreeProducer {
12831293 mHistograms .fill (HIST (" clusterMC_E_ConvertedPhoton" ), mcCluster.energy ());
12841294 mHistograms .fill (HIST (" clusterMC_m02_ConvertedPhoton" ), mcCluster.m02 ());
12851295 }
1296+
1297+ // determine properties fo particle that produced the given cluster
1298+ float mcIsolation = -1 ;
1299+ float mcEnergy = kInvalidValue ;
1300+ float mcPt = kInvalidValue ;
1301+ float mcEta = kInvalidValue ;
1302+ float mcPhi = kInvalidValue ;
1303+ if (mcIndex != -1 ) {
1304+ const auto & mcParticle = mcParticles.iteratorAt (mcIndex);
1305+ mcIsolation = ch_iso_in_cone (mcParticle, isoR, true );
1306+ mcEnergy = mcParticle.energy ();
1307+ mcPt = mcParticle.pt ();
1308+ mcEta = mcParticle.eta ();
1309+ mcPhi = mcParticle.phi ();
1310+ }
1311+
12861312 // fill table
1287- gammaMCInfosTable (storedColIndex, origin, leadingEnergyFraction);
1313+ gammaMCInfosTable (storedColIndex, origin, leadingEnergyFraction, mcEta, mcPhi, mcEnergy, mcPt, mcIsolation );
12881314 }
12891315 }
1290- PROCESS_SWITCH (GammaJetTreeProducer, processClustersMCInfo, " Process MC cluster information" , false );
12911316
12921317 // / \brief Fills the charged jet table with jet information and calculates jet properties
12931318 // / \param storedColIndex The stored collision index
@@ -1369,6 +1394,58 @@ struct GammaJetTreeProducer {
13691394 jetSubstructuresTable (storedColIndex, energyMotherVec, ptLeadingVec, ptSubLeadingVec, thetaVec);
13701395 }
13711396
1397+ // / \brief Fills the MC gen-level substructure table for each splitting in the jet
1398+ // / \param storedColIndex The stored collision index
1399+ // / \param jet The MC particle-level jet to process
1400+ // / \param mcparticles The MC particles collection
1401+ template <typename T, typename U>
1402+ void fillMCSubstructureTable (int32_t storedColIndex, T const & jet, U const & /* mcparticles*/ )
1403+ {
1404+ // adjust settings according to the jet radius
1405+ jetReclusterer.jetR = jet.r () / 100.0 ;
1406+ // clear vectors
1407+ energyMotherVec.clear ();
1408+ ptLeadingVec.clear ();
1409+ ptSubLeadingVec.clear ();
1410+ thetaVec.clear ();
1411+ jetReclustered.clear ();
1412+ jetConstituents.clear ();
1413+
1414+ if (jet.pt () < jetPtMin) {
1415+ return ;
1416+ }
1417+ for (const auto & jetConstituent : jet.template tracks_as <U>()) {
1418+ fastjetutilities::fillTracks (jetConstituent, jetConstituents, jetConstituent.globalIndex ());
1419+ }
1420+
1421+ fastjet::ClusterSequenceArea clusterSeq (jetReclusterer.findJets (jetConstituents, jetReclustered));
1422+ jetReclustered = sorted_by_pt (jetReclustered);
1423+
1424+ if (jetReclustered.size () == 0 ) {
1425+ return ;
1426+ }
1427+
1428+ fastjet::PseudoJet daughterSubJet = jetReclustered[0 ];
1429+ fastjet::PseudoJet parentSubJet1;
1430+ fastjet::PseudoJet parentSubJet2;
1431+
1432+ while (daughterSubJet.has_parents (parentSubJet1, parentSubJet2)) {
1433+ if (parentSubJet1.perp () < parentSubJet2.perp ()) {
1434+ std::swap (parentSubJet1, parentSubJet2);
1435+ }
1436+
1437+ energyMotherVec.push_back (daughterSubJet.e ());
1438+ ptLeadingVec.push_back (parentSubJet1.pt ());
1439+ ptSubLeadingVec.push_back (parentSubJet2.pt ());
1440+ thetaVec.push_back (parentSubJet1.delta_R (parentSubJet2));
1441+
1442+ // Continue with harder parent
1443+ daughterSubJet = parentSubJet1;
1444+ }
1445+
1446+ mcJetSubstructuresTable (storedColIndex, energyMotherVec, ptLeadingVec, ptSubLeadingVec, thetaVec);
1447+ }
1448+
13721449 Filter jetCuts = aod::jet::pt > jetPtMin;
13731450 // / \brief Processes charged jets and fills jet table
13741451 // / \param collision The collision to process
@@ -1395,7 +1472,6 @@ struct GammaJetTreeProducer {
13951472 }
13961473 }
13971474 }
1398- PROCESS_SWITCH (GammaJetTreeProducer, processChargedJetsData, " Process charged jets" , true );
13991475
14001476 Preslice<aod::JetParticles> ParticlesPerMCCollisions = aod::jmcparticle::mcCollisionId;
14011477 // / \brief Processes MC particles and fills MC particle table
@@ -1482,7 +1558,6 @@ struct GammaJetTreeProducer {
14821558 }
14831559 }
14841560 }
1485- PROCESS_SWITCH (GammaJetTreeProducer, processMCParticles, " Process MC particles" , false );
14861561
14871562 // NOTE: The KD tree is now built lazily in each function that needs it, so execution order is no longer critical
14881563 Preslice<aod::ChargedMCParticleLevelJets> PJetsPerMCCollisions = aod::jmcparticle::mcCollisionId;
@@ -1515,9 +1590,12 @@ struct GammaJetTreeProducer {
15151590 mcJetIndexMapping[pjet.globalIndex ()] = localIndex;
15161591 localIndex++;
15171592 mHistograms .fill (HIST (" mcpJetPt" ), pjet.pt ());
1593+
1594+ if (calculateJetSubstructure) {
1595+ fillMCSubstructureTable (storedColIndex, pjet, mcgenparticles);
1596+ }
15181597 }
15191598 }
1520- PROCESS_SWITCH (GammaJetTreeProducer, processChargedJetsMCP, " Process MC particle level jets" , false );
15211599
15221600 // NOTE: It is important that this function runs after the processChargedJetsMCP function (where the mc jet index mapping is built)
15231601 using JetMCPTable = soa::Filtered<soa::Join<aod::ChargedMCParticleLevelJets, aod::ChargedMCParticleLevelJetConstituents, aod::ChargedMCParticleLevelJetsMatchedToChargedMCDetectorLevelJets>>;
@@ -1564,6 +1642,16 @@ struct GammaJetTreeProducer {
15641642 chJetMCInfosTable (storedColIndex, iLocalIndexGeo, iLocalIndexPt);
15651643 }
15661644 }
1645+ // definition of all process switches, ordering is important!
1646+ PROCESS_SWITCH (GammaJetTreeProducer, processClearMaps, " process function that clears all the maps in each dataframe" , true ); // always run first
1647+ PROCESS_SWITCH (GammaJetTreeProducer, processMCCollisionsMatching, " Process MC event matching QA" , false );
1648+ PROCESS_SWITCH (GammaJetTreeProducer, processEventData, " Process event data" , true );
1649+ PROCESS_SWITCH (GammaJetTreeProducer, processEventMC, " Process MC event MC" , false );
1650+ PROCESS_SWITCH (GammaJetTreeProducer, processClusters, " Process EMCal clusters" , true );
1651+ PROCESS_SWITCH (GammaJetTreeProducer, processMCParticles, " Process MC particles" , false ); // has to run before processClustersMCInfo
1652+ PROCESS_SWITCH (GammaJetTreeProducer, processClustersMCInfo, " Process MC cluster information" , false );
1653+ PROCESS_SWITCH (GammaJetTreeProducer, processChargedJetsData, " Process charged jets" , true );
1654+ PROCESS_SWITCH (GammaJetTreeProducer, processChargedJetsMCP, " Process MC particle level jets" , false );
15671655 PROCESS_SWITCH (GammaJetTreeProducer, processChargedJetsMCD, " Process MC detector level jets" , false );
15681656};
15691657
0 commit comments