Skip to content

Commit c5d1739

Browse files
authored
[PWGUD] Modifications to UpcCandProdcucerGlobalMuon (#15590)
1 parent 03a7a28 commit c5d1739

File tree

1 file changed

+95
-51
lines changed

1 file changed

+95
-51
lines changed

PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <CCDB/BasicCCDBManager.h>
2424
#include <CCDB/CcdbApi.h>
2525
#include <CommonConstants/LHCConstants.h>
26+
#include <CommonConstants/PhysicsConstants.h>
2627
#include <DataFormatsParameters/GRPMagField.h>
2728
#include <DetectorsBase/GeometryManager.h>
2829
#include <DetectorsBase/Propagator.h>
@@ -87,8 +88,6 @@ struct UpcCandProducerGlobalMuon {
8788

8889
// NEW: MFT/Global track support configurables
8990
Configurable<bool> fEnableMFT{"fEnableMFT", true, "Enable MFT/global track processing"};
90-
Configurable<float> fMinEtaMFT{"fMinEtaMFT", -3.6, "Minimum eta for MFT acceptance"};
91-
Configurable<float> fMaxEtaMFT{"fMaxEtaMFT", -2.5, "Maximum eta for MFT acceptance"};
9291
Configurable<bool> fSaveMFTClusters{"fSaveMFTClusters", true, "Save MFT cluster information"};
9392

9493
// Ambiguous track propagation configurables
@@ -97,6 +96,8 @@ struct UpcCandProducerGlobalMuon {
9796
Configurable<float> fManualZShift{"fManualZShift", 0.0f, "Manual z-shift for global muon propagation to PV"};
9897
Configurable<float> fMaxDCAxy{"fMaxDCAxy", 999.f, "Maximum DCAxy for global muon track selection (cm)"};
9998
Configurable<int> fBcWindowCollision{"fBcWindowCollision", 4, "BC window for collision search for DCA-based vertex assignment"};
99+
Configurable<float> fMaxChi2MatchMCHMFT{"fMaxChi2MatchMCHMFT", 4.f, "Maximum chi2 for MCH-MFT matching quality filter"};
100+
Configurable<int> fBcWindowMCHMFT{"fBcWindowMCHMFT", 20, "BC window for searching MCH-MFT tracks around MCH-MID-MFT anchors"};
100101

101102
using ForwardTracks = o2::soa::Join<o2::aod::FwdTracks, o2::aod::FwdTracksCov>;
102103

@@ -140,14 +141,20 @@ struct UpcCandProducerGlobalMuon {
140141
histRegistry.get<TH1>(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(4, "GlobalFwd");
141142

142143
const AxisSpec axisEta{100, -4.0, -2.0, "#eta"};
143-
histRegistry.add("hEtaMFT", "MFT track eta", kTH1F, {axisEta});
144144
histRegistry.add("hEtaGlobal", "Global track eta", kTH1F, {axisEta});
145145

146146
const AxisSpec axisDCAxy{200, 0., 10., "DCA_{xy} (cm)"};
147147
const AxisSpec axisDCAz{200, -10., 10., "DCA_{z} (cm)"};
148148
histRegistry.add("hDCAxyGlobal", "DCAxy of global tracks to best collision", kTH1F, {axisDCAxy});
149149
histRegistry.add("hDCAzGlobal", "DCAz of global tracks to best collision", kTH1F, {axisDCAz});
150150
histRegistry.add("hNCompatColls", "Number of compatible collisions per global track", kTH1F, {{21, -0.5, 20.5}});
151+
152+
const AxisSpec axisChi2Match{200, 0., 100., "#chi^{2}_{MCH-MFT}"};
153+
histRegistry.add("hChi2MatchMCHMFT", "Chi2 of MCH-MFT matching (before cut)", kTH1F, {axisChi2Match});
154+
155+
const AxisSpec axisMass{500, 0., 10., "m_{inv} (GeV/c^{2})"};
156+
histRegistry.add("hMassGlobalMuon", "Invariant mass from MCH-MID-MFT tracks only", kTH1F, {axisMass});
157+
histRegistry.add("hMassGlobalMuonWithMCHMFT", "Invariant mass from MCH-MID-MFT + MCH-MFT tracks", kTH1F, {axisMass});
151158
}
152159
}
153160

@@ -482,12 +489,6 @@ struct UpcCandProducerGlobalMuon {
482489
}
483490
}
484491

485-
// NEW: Check if track is in MFT acceptance
486-
bool isInMFTAcceptance(float eta)
487-
{
488-
return (eta > fMinEtaMFT && eta < fMaxEtaMFT);
489-
}
490-
491492
// Propagate global muon track to collision vertex using helix propagation
492493
// and compute DCA (adapted from ambiguousTrackPropagation)
493494
// Returns {DCAxy, DCAz, DCAx, DCAy}
@@ -594,7 +595,8 @@ struct UpcCandProducerGlobalMuon {
594595

595596
std::map<uint64_t, std::vector<int64_t>> mapGlobalBcsWithMCHMIDTrackIds;
596597
std::map<uint64_t, std::vector<int64_t>> mapGlobalBcsWithMCHTrackIds;
597-
std::map<uint64_t, std::vector<int64_t>> mapGlobalBcsWithGlobalTrackIds; // NEW: For global tracks
598+
std::map<uint64_t, std::vector<int64_t>> mapGlobalBcsWithGlobalMuonTrackIds; // MCH-MID-MFT (good timing from MID)
599+
std::map<uint64_t, std::vector<int64_t>> mapGlobalBcsWithMCHMFTTrackIds; // MCH-MFT only (poor timing)
598600

599601
for (const auto& fwdTrack : fwdTracks) {
600602
auto trackType = fwdTrack.trackType();
@@ -614,8 +616,16 @@ struct UpcCandProducerGlobalMuon {
614616
mapGlobalBcsWithMCHMIDTrackIds[globalBC].push_back(trackId);
615617
} else if (trackType == MCHStandaloneTrack) { // MCH-only
616618
mapGlobalBcsWithMCHTrackIds[globalBC].push_back(trackId);
617-
} else if (fEnableMFT && (trackType == GlobalMuonTrack || trackType == GlobalForwardTrack)) { // NEW: Global tracks
618-
mapGlobalBcsWithGlobalTrackIds[globalBC].push_back(trackId);
619+
} else if (fEnableMFT && trackType == GlobalMuonTrack) { // MCH-MID-MFT: good timing, used as anchor
620+
histRegistry.fill(HIST("hChi2MatchMCHMFT"), fwdTrack.chi2MatchMCHMFT());
621+
if (fwdTrack.chi2MatchMCHMFT() > 0 && fwdTrack.chi2MatchMCHMFT() < fMaxChi2MatchMCHMFT) {
622+
mapGlobalBcsWithGlobalMuonTrackIds[globalBC].push_back(trackId);
623+
}
624+
} else if (fEnableMFT && trackType == GlobalForwardTrack) { // MCH-MFT: poor timing, matched to anchors
625+
histRegistry.fill(HIST("hChi2MatchMCHMFT"), fwdTrack.chi2MatchMCHMFT());
626+
if (fwdTrack.chi2MatchMCHMFT() > 0 && fwdTrack.chi2MatchMCHMFT() < fMaxChi2MatchMCHMFT) {
627+
mapGlobalBcsWithMCHMFTTrackIds[globalBC].push_back(trackId);
628+
}
619629
}
620630
}
621631

@@ -632,11 +642,13 @@ struct UpcCandProducerGlobalMuon {
632642

633643
int32_t candId = 0;
634644

635-
// NEW: Process global tracks if MFT is enabled
636-
if (fEnableMFT && !mapGlobalBcsWithGlobalTrackIds.empty()) {
637-
for (const auto& gbc_globalids : mapGlobalBcsWithGlobalTrackIds) {
638-
uint64_t globalBcGlobal = gbc_globalids.first;
639-
auto itFv0Id = mapGlobalBcWithV0A.find(globalBcGlobal);
645+
// Process global tracks: MCH-MID-MFT anchors + MCH-MFT in BC window
646+
// MCH-MID-MFT tracks have good timing (from MID) and serve as anchors.
647+
// MCH-MFT tracks have poor timing and are searched in a BC window around anchors.
648+
if (fEnableMFT && !mapGlobalBcsWithGlobalMuonTrackIds.empty()) {
649+
for (const auto& gbc_anchorids : mapGlobalBcsWithGlobalMuonTrackIds) {
650+
uint64_t globalBcAnchor = gbc_anchorids.first;
651+
auto itFv0Id = mapGlobalBcWithV0A.find(globalBcAnchor);
640652
if (itFv0Id != mapGlobalBcWithV0A.end()) {
641653
auto fv0Id = itFv0Id->second;
642654
const auto& fv0 = fv0s.iteratorAt(fv0Id);
@@ -647,17 +659,28 @@ struct UpcCandProducerGlobalMuon {
647659
continue;
648660
}
649661

650-
auto& vGlobalIds = gbc_globalids.second;
662+
auto& vAnchorIds = gbc_anchorids.second; // MCH-MID-MFT tracks at this BC
663+
664+
// Search MCH-MFT tracks in BC window around anchor (analogous to MCH-only matching)
665+
std::map<int64_t, uint64_t> mapMchMftIdBc{};
666+
getMchTrackIds(globalBcAnchor, mapGlobalBcsWithMCHMFTTrackIds, fBcWindowMCHMFT, mapMchMftIdBc);
667+
668+
// Collect all track IDs for vertex finding (anchors + matched MCH-MFT)
669+
std::vector<int64_t> allTrackIds;
670+
allTrackIds.reserve(vAnchorIds.size() + mapMchMftIdBc.size());
671+
for (const auto& id : vAnchorIds)
672+
allTrackIds.push_back(id);
673+
for (const auto& [id, gbc] : mapMchMftIdBc)
674+
allTrackIds.push_back(id);
651675

652676
// Step 1: Find best collision vertex using DCA-based propagation
653-
// (adapted from ambiguousTrackPropagation processMFTReassoc3D)
654677
float bestVtxX = 0., bestVtxY = 0., bestVtxZ = 0.;
655678
double bestAvgDCA = 999.;
656679
bool hasVertex = false;
657680
int nCompatColls = 0;
658681

659682
for (int dbc = -static_cast<int>(fBcWindowCollision); dbc <= static_cast<int>(fBcWindowCollision); dbc++) {
660-
uint64_t searchBC = globalBcGlobal + dbc;
683+
uint64_t searchBC = globalBcAnchor + dbc;
661684
auto itCol = mapGlobalBCtoCollisions.find(searchBC);
662685
if (itCol == mapGlobalBCtoCollisions.end())
663686
continue;
@@ -666,7 +689,7 @@ struct UpcCandProducerGlobalMuon {
666689
const auto& col = collisions.iteratorAt(colIdx);
667690
double sumDCAxy = 0.;
668691
int nTracks = 0;
669-
for (const auto& iglobal : vGlobalIds) {
692+
for (const auto& iglobal : allTrackIds) {
670693
const auto& trk = fwdTracks.iteratorAt(iglobal);
671694
auto dca = propagateGlobalToDCA(trk, col.posX(), col.posY(), col.posZ());
672695
sumDCAxy += dca[0];
@@ -685,60 +708,80 @@ struct UpcCandProducerGlobalMuon {
685708

686709
histRegistry.fill(HIST("hNCompatColls"), nCompatColls);
687710

688-
// Step 2: Select tracks with DCA quality cut
689-
std::vector<int64_t> tracksToSave;
690-
for (const auto& iglobal : vGlobalIds) {
691-
const auto& trk = fwdTracks.iteratorAt(iglobal);
692-
693-
// Apply DCA cut using best collision vertex
711+
// Step 2: Write anchor tracks (MCH-MID-MFT) with DCA quality cut
712+
constexpr double kMuonMass = o2::constants::physics::MassMuon;
713+
uint16_t numContrib = 0;
714+
double sumPx = 0., sumPy = 0., sumPz = 0., sumE = 0.;
715+
for (const auto& ianchor : vAnchorIds) {
694716
if (hasVertex) {
717+
const auto& trk = fwdTracks.iteratorAt(ianchor);
695718
auto dca = propagateGlobalToDCA(trk, bestVtxX, bestVtxY, bestVtxZ);
696719
histRegistry.fill(HIST("hDCAxyGlobal"), dca[0]);
697720
histRegistry.fill(HIST("hDCAzGlobal"), dca[1]);
698721
if (dca[0] > static_cast<double>(fMaxDCAxy))
699722
continue;
700723
}
724+
if (!addToFwdTable(candId, ianchor, globalBcAnchor, 0., fwdTracks, mcFwdTrackLabels))
725+
continue;
726+
const auto& trk = fwdTracks.iteratorAt(ianchor);
727+
double p2 = trk.px() * trk.px() + trk.py() * trk.py() + trk.pz() * trk.pz();
728+
sumPx += trk.px();
729+
sumPy += trk.py();
730+
sumPz += trk.pz();
731+
sumE += std::sqrt(p2 + kMuonMass * kMuonMass);
732+
numContrib++;
733+
selTrackIds.push_back(ianchor);
734+
}
701735

702-
// Check MFT acceptance and decide which track to use
703-
if (isInMFTAcceptance(trk.eta())) {
704-
// Inside MFT acceptance - use global track
705-
tracksToSave.push_back(iglobal);
706-
histRegistry.fill(HIST("hEtaMFT"), trk.eta());
707-
} else {
708-
// Outside MFT acceptance - look for MCH-MID counterpart
709-
auto itMid = mapGlobalBcsWithMCHMIDTrackIds.find(globalBcGlobal);
710-
if (itMid != mapGlobalBcsWithMCHMIDTrackIds.end()) {
711-
if (!itMid->second.empty()) {
712-
tracksToSave.push_back(itMid->second[0]);
713-
itMid->second.erase(itMid->second.begin());
714-
}
715-
}
716-
}
736+
// Fill invariant mass from MCH-MID-MFT anchors only
737+
uint16_t numContribAnch = numContrib;
738+
if (numContribAnch >= 2) {
739+
double mass2 = sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz;
740+
histRegistry.fill(HIST("hMassGlobalMuon"), mass2 > 0. ? std::sqrt(mass2) : 0.);
717741
}
718742

719-
// Step 3: Write tracks and event candidate with actual vertex position
720-
uint16_t numContrib = 0;
721-
for (const auto& trkId : tracksToSave) {
722-
if (!addToFwdTable(candId, trkId, globalBcGlobal, 0., fwdTracks, mcFwdTrackLabels))
743+
// Step 3: Write matched MCH-MFT tracks with DCA quality cut and adjusted track time
744+
for (const auto& [imchMft, gbc] : mapMchMftIdBc) {
745+
if (hasVertex) {
746+
const auto& trk = fwdTracks.iteratorAt(imchMft);
747+
auto dca = propagateGlobalToDCA(trk, bestVtxX, bestVtxY, bestVtxZ);
748+
histRegistry.fill(HIST("hDCAxyGlobal"), dca[0]);
749+
histRegistry.fill(HIST("hDCAzGlobal"), dca[1]);
750+
if (dca[0] > static_cast<double>(fMaxDCAxy))
751+
continue;
752+
}
753+
if (!addToFwdTable(candId, imchMft, gbc, (gbc - globalBcAnchor) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mcFwdTrackLabels))
723754
continue;
755+
const auto& trk = fwdTracks.iteratorAt(imchMft);
756+
double p2 = trk.px() * trk.px() + trk.py() * trk.py() + trk.pz() * trk.pz();
757+
sumPx += trk.px();
758+
sumPy += trk.py();
759+
sumPz += trk.pz();
760+
sumE += std::sqrt(p2 + kMuonMass * kMuonMass);
724761
numContrib++;
725-
selTrackIds.push_back(trkId);
762+
selTrackIds.push_back(imchMft);
763+
}
764+
765+
// Fill invariant mass including MCH-MFT tracks (only if MCH-MFT tracks were added)
766+
if (numContrib > numContribAnch && numContrib >= 2) {
767+
double mass2 = sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz;
768+
histRegistry.fill(HIST("hMassGlobalMuonWithMCHMFT"), mass2 > 0. ? std::sqrt(mass2) : 0.);
726769
}
727770

728771
if (numContrib < 1)
729772
continue;
730773

731-
eventCandidates(globalBcGlobal, runNumber, bestVtxX, bestVtxY, bestVtxZ, 0, numContrib, 0, 0);
774+
eventCandidates(globalBcAnchor, runNumber, bestVtxX, bestVtxY, bestVtxZ, 0, numContrib, 0, 0);
732775
std::vector<float> amplitudesV0A{};
733776
std::vector<int8_t> relBCsV0A{};
734777
std::vector<float> amplitudesT0A{};
735778
std::vector<int8_t> relBCsT0A{};
736779
if (nFV0s > 0) {
737-
getFV0Amplitudes(globalBcGlobal, fv0s, fBcWindowFITAmps, mapGlobalBcWithV0A, amplitudesV0A, relBCsV0A);
780+
getFV0Amplitudes(globalBcAnchor, fv0s, fBcWindowFITAmps, mapGlobalBcWithV0A, amplitudesV0A, relBCsV0A);
738781
}
739782
eventCandidatesSelsFwd(0., 0., amplitudesT0A, relBCsT0A, amplitudesV0A, relBCsV0A);
740783
if (nZdcs > 0) {
741-
auto itZDC = mapGlobalBcWithZdc.find(globalBcGlobal);
784+
auto itZDC = mapGlobalBcWithZdc.find(globalBcAnchor);
742785
if (itZDC != mapGlobalBcWithZdc.end()) {
743786
const auto& zdc = zdcs.iteratorAt(itZDC->second);
744787
float timeZNA = zdc.timeZNA();
@@ -821,7 +864,8 @@ struct UpcCandProducerGlobalMuon {
821864
vAmbFwdTrackIndexBCs.clear();
822865
mapGlobalBcsWithMCHMIDTrackIds.clear();
823866
mapGlobalBcsWithMCHTrackIds.clear();
824-
mapGlobalBcsWithGlobalTrackIds.clear();
867+
mapGlobalBcsWithGlobalMuonTrackIds.clear();
868+
mapGlobalBcsWithMCHMFTTrackIds.clear();
825869
mapGlobalBCtoCollisions.clear();
826870
selTrackIds.clear();
827871
}

0 commit comments

Comments
 (0)