Skip to content

Commit 19a89c5

Browse files
authored
[PWGUD] Using MFT for finding vertex for global muons following FwdDCAFitter (#15900)
1 parent ad554a7 commit 19a89c5

1 file changed

Lines changed: 53 additions & 125 deletions

File tree

PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx

Lines changed: 53 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@
1818
#include "PWGUD/Core/UPCHelpers.h"
1919
#include "PWGUD/DataModel/UDTables.h"
2020

21-
#include "Common/Core/fwdtrackUtilities.h"
22-
2321
#include <CCDB/BasicCCDBManager.h>
2422
#include <CCDB/CcdbApi.h>
2523
#include <CommonConstants/LHCConstants.h>
2624
#include <CommonConstants/PhysicsConstants.h>
25+
#include <DCAFitter/FwdDCAFitterN.h>
2726
#include <DataFormatsParameters/GRPMagField.h>
2827
#include <DetectorsBase/GeometryManager.h>
2928
#include <DetectorsBase/Propagator.h>
@@ -90,12 +89,12 @@ struct UpcCandProducerGlobalMuon {
9089
Configurable<bool> fEnableMFT{"fEnableMFT", true, "Enable MFT/global track processing"};
9190
Configurable<bool> fSaveMFTClusters{"fSaveMFTClusters", true, "Save MFT cluster information"};
9291

93-
// Ambiguous track propagation configurables
94-
Configurable<bool> fApplyZShiftFromCCDB{"fApplyZShiftFromCCDB", false, "Apply z-shift from CCDB for global muon propagation"};
95-
Configurable<std::string> fZShiftPath{"fZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z-shift"};
96-
Configurable<float> fManualZShift{"fManualZShift", 0.0f, "Manual z-shift for global muon propagation to PV"};
97-
Configurable<float> fMaxDCAxy{"fMaxDCAxy", 999.f, "Maximum DCAxy for global muon track selection (cm)"};
98-
Configurable<int> fBcWindowCollision{"fBcWindowCollision", 4, "BC window for collision search for DCA-based vertex assignment"};
92+
// FwdDCAFitter configurables
93+
Configurable<bool> fPropagateToPCA{"fPropagateToPCA", true, "Propagate tracks to PCA"};
94+
Configurable<float> fMaxR{"fMaxR", 200.f, "Maximum radius for FwdDCAFitter (cm)"};
95+
Configurable<float> fMinParamChange{"fMinParamChange", 1.0e-3, "Minimum parameter change for FwdDCAFitter convergence"};
96+
Configurable<float> fMinRelChi2Change{"fMinRelChi2Change", 0.9, "Minimum relative chi2 change for FwdDCAFitter convergence"};
97+
Configurable<bool> fUseAbsDCA{"fUseAbsDCA", true, "Use absolute DCA in FwdDCAFitter"};
9998
Configurable<float> fMaxChi2MatchMCHMFT{"fMaxChi2MatchMCHMFT", 4.f, "Maximum chi2 for MCH-MFT matching quality filter"};
10099
Configurable<int> fBcWindowMCHMFT{"fBcWindowMCHMFT", 20, "BC window for searching MCH-MFT tracks around MCH-MID-MFT anchors"};
101100

@@ -108,13 +107,10 @@ struct UpcCandProducerGlobalMuon {
108107
o2::ccdb::CcdbApi fCCDBApi;
109108
o2::globaltracking::MatchGlobalFwd fMatching;
110109

111-
// Ambiguous track propagation members
112-
float fBz{0}; // Magnetic field at MFT center
113-
static constexpr double fCcenterMFT[3] = {0, 0, -61.4}; // Field evaluation point at center of MFT
114-
float fZShift{0}; // z-vertex shift for forward track propagation
110+
// FwdDCAFitter member
111+
o2::vertexing::FwdDCAFitterN<2> fFwdFitter;
115112

116113
// Named constants (avoid magic numbers in expressions)
117-
static constexpr double kInvalidDCA = 999.; // Sentinel for "no valid DCA computed yet"
118114
static constexpr double kBcTimeRoundingOffset = 1.; // Offset used when rounding trackTime to BC units
119115
static constexpr uint16_t kMinTracksForPair = 2; // Minimum tracks required to compute a pair invariant mass
120116
static constexpr uint16_t kMinTracksForCandidate = 1; // Minimum contributors required to save a candidate
@@ -148,11 +144,8 @@ struct UpcCandProducerGlobalMuon {
148144
const AxisSpec axisEta{100, -4.0, -2.0, "#eta"};
149145
histRegistry.add("hEtaGlobal", "Global track eta", kTH1F, {axisEta});
150146

151-
const AxisSpec axisDCAxy{200, 0., 10., "DCA_{xy} (cm)"};
152-
const AxisSpec axisDCAz{200, -10., 10., "DCA_{z} (cm)"};
153-
histRegistry.add("hDCAxyGlobal", "DCAxy of global tracks to best collision", kTH1F, {axisDCAxy});
154-
histRegistry.add("hDCAzGlobal", "DCAz of global tracks to best collision", kTH1F, {axisDCAz});
155-
histRegistry.add("hNCompatColls", "Number of compatible collisions per global track", kTH1F, {{21, -0.5, 20.5}});
147+
const AxisSpec axisChi2PCA{200, 0., 100., "#chi^{2}_{PCA}"};
148+
histRegistry.add("hChi2PCA", "Chi2 at PCA from FwdDCAFitter", kTH1F, {axisChi2PCA});
156149

157150
const AxisSpec axisChi2Match{200, 0., 100., "#chi^{2}_{MCH-MFT}"};
158151
histRegistry.add("hChi2MatchMCHMFT", "Chi2 of MCH-MFT matching (before cut)", kTH1F, {axisChi2Match});
@@ -409,11 +402,9 @@ struct UpcCandProducerGlobalMuon {
409402
track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalForwardTrack;
410403
o2::dataformats::GlobalFwdTrack pft;
411404
if (isGlobal && fEnableMFT) {
412-
// For global tracks, use MFT helix propagation to vertex instead of
413-
// MCH extrapolation, which fails because the track z is upstream of
414-
// the front absorber (z ~ -60 cm vs absorber at z ~ -90 cm)
415-
o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(track, fZShift);
416-
trackPar.propagateToZhelix(0., fBz);
405+
// For global tracks, use raw kinematics for cuts;
406+
// the FwdDCAFitter handles proper vertex propagation
407+
auto trackPar = fwdToTrackPar(track);
417408
pft.setParameters(trackPar.getParameters());
418409
pft.setZ(trackPar.getZ());
419410
pft.setCovariances(trackPar.getCovariances());
@@ -491,34 +482,17 @@ struct UpcCandProducerGlobalMuon {
491482
}
492483
}
493484

494-
// Propagate global muon track to collision vertex using helix propagation
495-
// and compute DCA (adapted from ambiguousTrackPropagation)
496-
// Returns {DCAxy, DCAz, DCAx, DCAy}
497-
std::array<double, 4> propagateGlobalToDCA(ForwardTracks::iterator const& track,
498-
double colX, double colY, double colZ)
485+
// Convert forward track to TrackParCovFwd for the FwdDCAFitter
486+
o2::track::TrackParCovFwd fwdToTrackPar(ForwardTracks::iterator const& track)
499487
{
500-
o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(track, fZShift);
501-
std::array<double, 3> dcaOrig;
502-
trackPar.propagateToDCAhelix(fBz, {colX, colY, colZ}, dcaOrig);
503-
double dcaXY = std::sqrt(dcaOrig[0] * dcaOrig[0] + dcaOrig[1] * dcaOrig[1]);
504-
return {dcaXY, dcaOrig[2], dcaOrig[0], dcaOrig[1]};
505-
}
506-
507-
// Dispatch DCA computation: use MFT helix propagation when fEnableMFT is true,
508-
// otherwise fall back to MCH extrapolation to (0,0,0) via propagateToZero.
509-
// Returns {DCAxy, DCAz, DCAx, DCAy}
510-
std::array<double, 4> propagateFwdToDCA(ForwardTracks::iterator const& track,
511-
double colX, double colY, double colZ)
512-
{
513-
if (fEnableMFT) {
514-
return propagateGlobalToDCA(track, colX, colY, colZ);
515-
}
516-
auto pft = propagateToZero(track);
517-
double dcaX = pft.getX() - colX;
518-
double dcaY = pft.getY() - colY;
519-
double dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY);
520-
double dcaZ = pft.getZ() - colZ;
521-
return {dcaXY, dcaZ, dcaX, dcaY};
488+
using SMatrix5 = ROOT::Math::SVector<double, 5>;
489+
using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>;
490+
SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt());
491+
std::vector<double> v1{track.cXX(), track.cXY(), track.cYY(), track.cPhiX(), track.cPhiY(),
492+
track.cPhiPhi(), track.cTglX(), track.cTglY(), track.cTglPhi(), track.cTglTgl(),
493+
track.c1PtX(), track.c1PtY(), track.c1PtPhi(), track.c1PtTgl(), track.c1Pt21Pt2()};
494+
SMatrix55 tcovs(v1.begin(), v1.end());
495+
return o2::track::TrackParCovFwd{track.z(), tpars, tcovs, track.chi2()};
522496
}
523497

524498
void createCandidates(ForwardTracks const& fwdTracks,
@@ -549,24 +523,18 @@ struct UpcCandProducerGlobalMuon {
549523
if (fUpcCuts.getUseFwdCuts()) {
550524
o2::mch::TrackExtrap::setField();
551525
}
552-
// Initialize MFT magnetic field and z-shift for ambiguous track propagation
526+
// Initialize FwdDCAFitter with magnetic field
553527
if (fEnableMFT) {
554528
o2::field::MagneticField* field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField());
555-
fBz = field->getBz(fCcenterMFT);
556-
LOG(info) << "Magnetic field at MFT center: bZ = " << fBz;
557-
if (fApplyZShiftFromCCDB) {
558-
auto* zShift = fCCDB->getForTimeStamp<std::vector<float>>(fZShiftPath, ts);
559-
if (zShift != nullptr && !zShift->empty()) {
560-
fZShift = (*zShift)[0];
561-
LOGF(info, "z-shift from CCDB: %f cm", fZShift);
562-
} else {
563-
fZShift = 0;
564-
LOGF(info, "z-shift not found in CCDB path %s, set to 0 cm", fZShiftPath.value);
565-
}
566-
} else {
567-
fZShift = fManualZShift;
568-
LOGF(info, "z-shift manually set to %f cm", fZShift);
569-
}
529+
static constexpr double kCenterMFT[3] = {0, 0, -61.4};
530+
float bz = field->getBz(kCenterMFT);
531+
LOG(info) << "FwdDCAFitter magnetic field at MFT center: bZ = " << bz;
532+
fFwdFitter.setBz(bz);
533+
fFwdFitter.setPropagateToPCA(fPropagateToPCA);
534+
fFwdFitter.setMaxR(fMaxR);
535+
fFwdFitter.setMinParamChange(fMinParamChange);
536+
fFwdFitter.setMinRelChi2Change(fMinRelChi2Change);
537+
fFwdFitter.setUseAbsDCA(fUseAbsDCA);
570538
}
571539
}
572540
}
@@ -648,14 +616,7 @@ struct UpcCandProducerGlobalMuon {
648616
}
649617
}
650618

651-
// Map global BC to collisions for DCA-based vertex assignment
652-
std::map<uint64_t, std::vector<int64_t>> mapGlobalBCtoCollisions;
653-
for (const auto& col : collisions) {
654-
uint64_t gbc = vGlobalBCs[col.bcId()];
655-
mapGlobalBCtoCollisions[gbc].push_back(col.globalIndex());
656-
}
657-
658-
std::vector<int64_t> selTrackIds{}; // NEW: For cluster saving
619+
std::vector<int64_t> selTrackIds{}; // For cluster saving
659620

660621
int32_t candId = 0;
661622

@@ -682,62 +643,38 @@ struct UpcCandProducerGlobalMuon {
682643
std::map<int64_t, uint64_t> mapMchMftIdBc{};
683644
getMchTrackIds(globalBcAnchor, mapGlobalBcsWithMCHMFTTrackIds, fBcWindowMCHMFT, mapMchMftIdBc);
684645

685-
// Collect all track IDs for vertex finding (anchors + matched MCH-MFT)
646+
// Collect all track IDs (anchors + matched MCH-MFT)
686647
std::vector<int64_t> allTrackIds;
687648
allTrackIds.reserve(vAnchorIds.size() + mapMchMftIdBc.size());
688649
for (const auto& id : vAnchorIds)
689650
allTrackIds.push_back(id);
690651
for (const auto& [id, gbc] : mapMchMftIdBc)
691652
allTrackIds.push_back(id);
692653

693-
// Step 1: Find best collision vertex using DCA-based propagation
654+
// Step 1: Find secondary vertex using FwdDCAFitter on the first track pair
694655
float bestVtxX = 0., bestVtxY = 0., bestVtxZ = 0.;
695-
double bestAvgDCA = kInvalidDCA;
696-
bool hasVertex = false;
697-
int nCompatColls = 0;
698-
699-
for (int dbc = -static_cast<int>(fBcWindowCollision); dbc <= static_cast<int>(fBcWindowCollision); dbc++) {
700-
uint64_t searchBC = globalBcAnchor + dbc;
701-
auto itCol = mapGlobalBCtoCollisions.find(searchBC);
702-
if (itCol == mapGlobalBCtoCollisions.end())
703-
continue;
704-
for (const auto& colIdx : itCol->second) {
705-
nCompatColls++;
706-
const auto& col = collisions.iteratorAt(colIdx);
707-
double sumDCAxy = 0.;
708-
int nTracks = 0;
709-
for (const auto& iglobal : allTrackIds) {
710-
const auto& trk = fwdTracks.iteratorAt(iglobal);
711-
auto dca = propagateFwdToDCA(trk, col.posX(), col.posY(), col.posZ());
712-
sumDCAxy += dca[0];
713-
nTracks++;
714-
}
715-
double avgDCA = nTracks > 0 ? sumDCAxy / nTracks : kInvalidDCA;
716-
if (!hasVertex || avgDCA < bestAvgDCA) {
717-
bestAvgDCA = avgDCA;
718-
bestVtxX = col.posX();
719-
bestVtxY = col.posY();
720-
bestVtxZ = col.posZ();
721-
hasVertex = true;
722-
}
656+
657+
if (allTrackIds.size() >= kMinTracksForPair) {
658+
const auto& trk1 = fwdTracks.iteratorAt(allTrackIds[0]);
659+
const auto& trk2 = fwdTracks.iteratorAt(allTrackIds[1]);
660+
auto pars1 = fwdToTrackPar(trk1);
661+
auto pars2 = fwdToTrackPar(trk2);
662+
int procCode = fFwdFitter.process(pars1, pars2);
663+
if (procCode != 0) {
664+
auto secondaryVertex = fFwdFitter.getPCACandidate();
665+
auto chi2PCA = fFwdFitter.getChi2AtPCACandidate();
666+
bestVtxX = secondaryVertex[0];
667+
bestVtxY = secondaryVertex[1];
668+
bestVtxZ = secondaryVertex[2];
669+
histRegistry.fill(HIST("hChi2PCA"), chi2PCA);
723670
}
724671
}
725672

726-
histRegistry.fill(HIST("hNCompatColls"), nCompatColls);
727-
728-
// Step 2: Write anchor tracks (MCH-MID-MFT) with DCA quality cut
673+
// Step 2: Write anchor tracks (MCH-MID-MFT)
729674
constexpr double kMuonMass = o2::constants::physics::MassMuon;
730675
uint16_t numContrib = 0;
731676
double sumPx = 0., sumPy = 0., sumPz = 0., sumE = 0.;
732677
for (const auto& ianchor : vAnchorIds) {
733-
if (hasVertex) {
734-
const auto& trk = fwdTracks.iteratorAt(ianchor);
735-
auto dca = propagateFwdToDCA(trk, bestVtxX, bestVtxY, bestVtxZ);
736-
histRegistry.fill(HIST("hDCAxyGlobal"), dca[0]);
737-
histRegistry.fill(HIST("hDCAzGlobal"), dca[1]);
738-
if (dca[0] > static_cast<double>(fMaxDCAxy))
739-
continue;
740-
}
741678
if (!addToFwdTable(candId, ianchor, globalBcAnchor, 0., fwdTracks, mcFwdTrackLabels))
742679
continue;
743680
const auto& trk = fwdTracks.iteratorAt(ianchor);
@@ -757,16 +694,8 @@ struct UpcCandProducerGlobalMuon {
757694
histRegistry.fill(HIST("hMassGlobalMuon"), mass2 > 0. ? std::sqrt(mass2) : 0.);
758695
}
759696

760-
// Step 3: Write matched MCH-MFT tracks with DCA quality cut and adjusted track time
697+
// Step 3: Write matched MCH-MFT tracks with adjusted track time
761698
for (const auto& [imchMft, gbc] : mapMchMftIdBc) {
762-
if (hasVertex) {
763-
const auto& trk = fwdTracks.iteratorAt(imchMft);
764-
auto dca = propagateFwdToDCA(trk, bestVtxX, bestVtxY, bestVtxZ);
765-
histRegistry.fill(HIST("hDCAxyGlobal"), dca[0]);
766-
histRegistry.fill(HIST("hDCAzGlobal"), dca[1]);
767-
if (dca[0] > static_cast<double>(fMaxDCAxy))
768-
continue;
769-
}
770699
if (!addToFwdTable(candId, imchMft, gbc, (gbc - globalBcAnchor) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mcFwdTrackLabels))
771700
continue;
772701
const auto& trk = fwdTracks.iteratorAt(imchMft);
@@ -883,7 +812,6 @@ struct UpcCandProducerGlobalMuon {
883812
mapGlobalBcsWithMCHTrackIds.clear();
884813
mapGlobalBcsWithGlobalMuonTrackIds.clear();
885814
mapGlobalBcsWithMCHMFTTrackIds.clear();
886-
mapGlobalBCtoCollisions.clear();
887815
selTrackIds.clear();
888816
}
889817

0 commit comments

Comments
 (0)