2929#include " Common/DataModel/PIDResponseTPC.h"
3030#include " Common/DataModel/TrackSelectionTables.h"
3131
32+ #include " CCDB/BasicCCDBManager.h"
33+ #include " DataFormatsParameters/GRPMagField.h"
3234#include " Framework/ASoAHelpers.h"
3335#include " Framework/AnalysisDataModel.h"
3436#include " Framework/AnalysisTask.h"
@@ -62,13 +64,11 @@ using namespace constants::physics;
6264using BCsRun3 = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels,
6365 aod::Run3MatchedToBCSparse>;
6466
65- // =============================================================================
66- // Main Analysis Struct
67- // =============================================================================
6867struct MultiplicityPt {
6968
7069 // Service
7170 Service<o2::framework::O2DatabasePDG> pdg;
71+ Service<ccdb::BasicCCDBManager> ccdb;
7272 static constexpr int CentBinMax = 100 ;
7373 static constexpr int MultBinMax = 200 ;
7474 static constexpr int RecMultBinMax = 100 ;
@@ -82,9 +82,6 @@ struct MultiplicityPt {
8282
8383 };
8484
85- // ===========================================================================
86- // Configurable Parameters
87- // ===========================================================================
8885 Configurable<bool > isRun3{" isRun3" , true , " is Run3 dataset" };
8986 Configurable<float > cfgCutVertex{" cfgCutVertex" , 10 .0f , " Accepted z-vertex range" };
9087 Configurable<int > cfgINELCut{" cfgINELCut" , 0 , " INEL event selection: 0 no sel, 1 INEL>0, 2 INEL>1" };
@@ -123,7 +120,12 @@ struct MultiplicityPt {
123120 Configurable<bool > nClTPCPIDCut{" nClTPCPIDCut" , true , " Apply TPC clusters for PID cut" };
124121
125122 // Phi cut parameters
126- Configurable<bool > applyPhiCut{" applyPhiCut" , false , " Apply phi sector cut" };
123+ Configurable<bool > applyPhiCut{" applyPhiCut" , false , " Apply phi sector cut to remove problematic TPC regions" };
124+ Configurable<float > pTthresholdPhiCut{" pTthresholdPhiCut" , 2 .0f , " pT threshold above which to apply phi cut" };
125+ Configurable<double > phiCutLowParam1{" phiCutLowParam1" , 0.119297 , " First parameter for low phi cut" };
126+ Configurable<double > phiCutLowParam2{" phiCutLowParam2" , 0.000379693 , " Second parameter for low phi cut" };
127+ Configurable<double > phiCutHighParam1{" phiCutHighParam1" , 0.16685 , " First parameter for high phi cut" };
128+ Configurable<double > phiCutHighParam2{" phiCutHighParam2" , 0.00981942 , " Second parameter for high phi cut" };
127129
128130 // Basic track cuts
129131 Configurable<float > cfgTrkEtaCut{" cfgTrkEtaCut" , 0 .8f , " Eta range for tracks" };
@@ -136,14 +138,14 @@ struct MultiplicityPt {
136138
137139 // Custom track cuts matching spectraTOF
138140 TrackSelection customTrackCuts;
141+
142+ // TF1 pointers for phi cuts
143+ TF1* fphiCutLow = nullptr ;
144+ TF1* fphiCutHigh = nullptr ;
139145
140146 // Histogram Registry
141147 HistogramRegistry ue;
142148
143- // ===========================================================================
144- // Table Definitions - Using individual tables, not joined for MC
145- // ===========================================================================
146-
147149 // Data collisions (not used but kept for completeness)
148150 using CollisionTableData = soa::Join<aod::Collisions, aod::EvSels, aod::McCentFT0Ms>;
149151
@@ -165,9 +167,6 @@ struct MultiplicityPt {
165167 // Preslice for MC particles
166168 Preslice<aod::McParticles> perMCCol = aod::mcparticle::mcCollisionId;
167169
168- // ===========================================================================
169- // Constants
170- // ===========================================================================
171170 enum ParticleSpecies : int {
172171 kPion = 0 ,
173172 kKaon = 1 ,
@@ -179,9 +178,69 @@ struct MultiplicityPt {
179178 static constexpr int PDGKaon = kKPlus ;
180179 static constexpr int PDGProton = kProton ;
181180
182- // ===========================================================================
183- // Helper Functions
184- // ===========================================================================
181+
182+ // Get magnetic field from CCDB
183+ int getMagneticField (uint64_t timestamp)
184+ {
185+ static o2::parameters::GRPMagField* grpo = nullptr ;
186+ if (grpo == nullptr ) {
187+ grpo = ccdb->getForTimeStamp <o2::parameters::GRPMagField>(" GLO/Config/GRPMagField" , timestamp);
188+ if (grpo == nullptr ) {
189+ LOGF (fatal, " GRP object not found for timestamp %llu" , timestamp);
190+ return 0 ;
191+ }
192+ LOGF (info, " Retrieved GRP for timestamp %llu with magnetic field of %d kG" , timestamp, grpo->getNominalL3Field ());
193+ }
194+ return grpo->getNominalL3Field ();
195+ }
196+
197+ // Get transformed phi for phi cut (with magnetic field)
198+ float getTransformedPhi (const float phi, const int charge, const float magField) const
199+ {
200+ float transformedPhi = phi;
201+ if (magField < 0 ) {
202+ transformedPhi = o2::constants::math::TwoPI - transformedPhi;
203+ }
204+ if (charge < 0 ) {
205+ transformedPhi = o2::constants::math::TwoPI - transformedPhi;
206+ }
207+ transformedPhi += o2::constants::math::PI / 18 .0f ;
208+ transformedPhi = std::fmod (transformedPhi, o2::constants::math::PI / 9 .0f );
209+ return transformedPhi;
210+ }
211+
212+ // Phi cut function (with magnetic field)
213+ template <typename TrackType>
214+ bool passedPhiCut (const TrackType& track, float magField) const
215+ {
216+ if (!applyPhiCut.value ) {
217+ return true ;
218+ }
219+
220+ if (track.pt () < pTthresholdPhiCut.value ) {
221+ return true ;
222+ }
223+
224+ float pt = track.pt ();
225+ float phi = track.phi ();
226+ int charge = track.sign ();
227+
228+ if (magField < 0 ) {
229+ phi = o2::constants::math::TwoPI - phi;
230+ }
231+ if (charge < 0 ) {
232+ phi = o2::constants::math::TwoPI - phi;
233+ }
234+
235+ phi += o2::constants::math::PI / 18 .0f ;
236+ phi = std::fmod (phi, o2::constants::math::PI / 9 .0f );
237+
238+ if (phi < fphiCutHigh->Eval (pt) && phi > fphiCutLow->Eval (pt)) {
239+ return false ;
240+ }
241+
242+ return true ;
243+ }
185244
186245 template <typename ParticleContainer>
187246 int countGeneratedChargedPrimaries (const ParticleContainer& particles, float etaMax, float ptMin) const
@@ -257,7 +316,7 @@ struct MultiplicityPt {
257316 }
258317
259318 template <typename TrackType>
260- bool passesTrackSelection (TrackType const & track) const
319+ bool passesTrackSelection (TrackType const & track, float magField = 0 ) const
261320 {
262321 if (track.eta () < cfgCutEtaMin.value || track.eta () > cfgCutEtaMax.value )
263322 return false ;
@@ -276,6 +335,10 @@ struct MultiplicityPt {
276335
277336 if (!passedNClTPCPIDCut (track))
278337 return false ;
338+
339+ // Add phi cut with magnetic field
340+ if (!passedPhiCut (track, magField))
341+ return false ;
279342
280343 return true ;
281344 }
@@ -348,9 +411,7 @@ struct MultiplicityPt {
348411 return true ;
349412 }
350413
351- // ===========================================================================
352- // Process Switches
353- // ===========================================================================
414+
354415 void processData (CollisionTableData::iterator const & collision,
355416 TrackTableData const & tracks,
356417 BCsRun3 const & bcs);
@@ -365,9 +426,7 @@ struct MultiplicityPt {
365426 BCsRun3 const & bcs);
366427 PROCESS_SWITCH (MultiplicityPt, processMC, " process MC" , true );
367428
368- // ===========================================================================
369- // Standard Framework Functions
370- // ===========================================================================
429+
371430 void init (InitContext const &);
372431
373432 void endOfStream (EndOfStreamContext& /* eos*/ )
@@ -382,24 +441,38 @@ struct MultiplicityPt {
382441 }
383442};
384443
385- // =============================================================================
386- // Workflow Definition
387- // =============================================================================
444+
388445WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
389446{
390447 return WorkflowSpec{adaptAnalysisTask<MultiplicityPt>(cfgc)};
391448}
392449
393- // =============================================================================
394- // Implementation of Member Functions
395- // =============================================================================
396450
397451void MultiplicityPt::init (InitContext const &)
398452{
399453 LOG (info) << " ==================================================" ;
400454 LOG (info) << " Initializing MultiplicityPt task with full centrality diagnostics" ;
401455 LOG (info) << " ==================================================" ;
402456
457+ // Initialize phi cut functions
458+ if (applyPhiCut.value ) {
459+ fphiCutLow = new TF1 (" StandardPhiCutLow" ,
460+ Form (" %f/x/x+pi/18.0-%f" ,
461+ phiCutLowParam1.value , phiCutLowParam2.value ),
462+ 0 , 50 );
463+ fphiCutHigh = new TF1 (" StandardPhiCutHigh" ,
464+ Form (" %f/x+pi/18.0+%f" ,
465+ phiCutHighParam1.value , phiCutHighParam2.value ),
466+ 0 , 50 );
467+
468+ LOGF (info, " === Phi Cut Parameters ===" );
469+ LOGF (info, " Low cut: %.6f/x² + pi/18 - %.6f" ,
470+ phiCutLowParam1.value , phiCutLowParam2.value );
471+ LOGF (info, " High cut: %.6f/x + pi/18 + %.6f" ,
472+ phiCutHighParam1.value , phiCutHighParam2.value );
473+ LOGF (info, " Applied for pT > %.1f GeV/c" , pTthresholdPhiCut.value );
474+ }
475+
403476 if (useCustomTrackCuts.value ) {
404477 LOG (info) << " Using custom track cuts matching spectraTOF approach" ;
405478 customTrackCuts = getGlobalTrackSelectionRun3ITSMatch (itsPattern.value );
@@ -448,10 +521,6 @@ void MultiplicityPt::init(InitContext const&)
448521 }
449522 AxisSpec recoMultAxis = {recoMultBins, " N_{ch}^{reco}" };
450523
451- // ===========================================================================
452- // Comprehensive Histogram Registration
453- // ===========================================================================
454-
455524 // Centrality diagnostic histograms - USE FINE BINNING
456525 ue.add (" Centrality/hCentRaw" , " Raw FT0M Centrality (no cuts);Centrality (%);Counts" ,
457526 HistType::kTH1D , {centFineAxis});
@@ -578,6 +647,16 @@ void MultiplicityPt::init(InitContext const&)
578647 ue.add (" Inclusive/hPtMeasuredVsCent" , " All measured tracks (PID) vs centrality;#it{p}_{T} (GeV/#it{c});FT0M Centrality (%)" ,
579648 HistType::kTH2D , {ptAxis, centAxis});
580649
650+ // Phi cut monitoring histograms
651+ if (applyPhiCut.value ) {
652+ ue.add (" PhiCut/hPtVsPhiPrimeBefore" , " pT vs φ' before cut;p_{T} (GeV/c);φ'" ,
653+ HistType::kTH2F , {{100 , 0 , 10 }, {100 , 0 , 0.4 }});
654+ ue.add (" PhiCut/hPtVsPhiPrimeAfter" , " pT vs φ' after cut;p_{T} (GeV/c);φ'" ,
655+ HistType::kTH2F , {{100 , 0 , 10 }, {100 , 0 , 0.4 }});
656+ ue.add (" PhiCut/hRejectionRate" , " Track rejection rate by phi cut;p_{T} (GeV/c);Rejection Rate" ,
657+ HistType::kTProfile , {{100 , 0 , 10 }});
658+ }
659+
581660 // Particle-specific histograms
582661 const std::array<std::string, kNSpecies > particleNames = {" Pion" , " Kaon" , " Proton" };
583662 const std::array<std::string, kNSpecies > particleSymbols = {" #pi^{#pm}" , " K^{#pm}" , " p+#bar{p}" };
@@ -654,12 +733,11 @@ void MultiplicityPt::init(InitContext const&)
654733 LOG (info) << " === Initialized MultiplicityPt task with full centrality diagnostics ===" ;
655734 LOG (info) << " Standard centrality binning: " << centBinningStd.size () - 1 << " bins (0-100%)" ;
656735 LOG (info) << " Fine centrality binning: " << centBinningFine.size () - 1 << " bins (0-100%)" ;
736+ if (applyPhiCut.value ) {
737+ LOG (info) << " Phi cut ENABLED for pT > " << pTthresholdPhiCut.value << " GeV/c" ;
738+ }
657739}
658740
659- // =============================================================================
660- // Process Functions
661- // =============================================================================
662-
663741void MultiplicityPt::processData (CollisionTableData::iterator const & /* collision*/ ,
664742 TrackTableData const & /* tracks*/ ,
665743 BCsRun3 const & /* bcs*/ )
@@ -681,9 +759,7 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
681759 LOG (info) << " Total collision labels: " << labels.size ();
682760 LOG (info) << " Total centrality entries: " << centTable.size ();
683761
684- // ===========================================================================
685- // DEBUG: Print raw centrality information first
686- // ===========================================================================
762+
687763 LOG (info) << " \n === CENTRALITY DEBUG - RAW DATA ===" ;
688764 LOG (info) << " First 20 centrality values from centTable:" ;
689765 int debugCount = 0 ;
@@ -714,9 +790,7 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
714790 LOG (info) << " Checking if centrality might be inverted..." ;
715791 LOG (info) << " Will check correlation with multiplicity in the next step." ;
716792
717- // ===========================================================================
718- // FIRST PASS: Build maps of MC collision ID to generated particle counts
719- // ===========================================================================
793+
720794 std::map<int64_t , int > mcCollisionToNch;
721795 std::map<int64_t , float > mcCollisionVz;
722796 std::set<int64_t > physicsSelectedMCCollisions;
@@ -797,9 +871,6 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
797871 LOG (info) << " INEL>1: " << mcINELgt1;
798872 LOG (info) << " Physics-selected MC collisions: " << physicsSelectedMCCollisions.size ();
799873
800- // ===========================================================================
801- // Build maps for labels and centrality
802- // ===========================================================================
803874 std::map<int64_t , int64_t > recoToMcMap;
804875 std::map<int64_t , float > recoToCentMap;
805876
@@ -837,9 +908,7 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
837908 LOG (info) << " recoToMcMap size: " << recoToMcMap.size ();
838909 LOG (info) << " recoToCentMap size: " << recoToCentMap.size ();
839910
840- // ===========================================================================
841- // DEBUG: Check correlation between centrality and multiplicity
842- // ===========================================================================
911+
843912 LOG (info) << " \n === CENTRALITY VS MULTIPLICITY DEBUG ===" ;
844913
845914 // Create temporary vectors to check correlation
@@ -1003,6 +1072,13 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
10031072 nSelectedEvents++;
10041073 nPassAll++;
10051074
1075+ // Get magnetic field for phi cut
1076+ float magField = 0 ;
1077+ if (applyPhiCut.value ) {
1078+ const auto & bc = collision.bc_as <BCsRun3>();
1079+ magField = getMagneticField (bc.timestamp ());
1080+ }
1081+
10061082 // Process tracks in selected events
10071083 int nTracksInEvent = 0 ;
10081084 for (const auto & track : tracks) {
@@ -1011,9 +1087,22 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks,
10111087 if (track.collisionId () != collId)
10121088 continue ;
10131089
1014- if (!passesTrackSelection (track)) {
1090+ // Fill phi cut monitoring before cut
1091+ if (applyPhiCut.value && track.pt () >= pTthresholdPhiCut.value ) {
1092+ float phiPrime = getTransformedPhi (track.phi (), track.sign (), magField);
1093+ ue.fill (HIST (" PhiCut/hPtVsPhiPrimeBefore" ), track.pt (), phiPrime);
1094+ }
1095+
1096+ if (!passesTrackSelection (track, magField)) {
10151097 continue ;
10161098 }
1099+
1100+ // Fill phi cut monitoring after cut
1101+ if (applyPhiCut.value && track.pt () >= pTthresholdPhiCut.value ) {
1102+ float phiPrime = getTransformedPhi (track.phi (), track.sign (), magField);
1103+ ue.fill (HIST (" PhiCut/hPtVsPhiPrimeAfter" ), track.pt (), phiPrime);
1104+ }
1105+
10171106 nTracksInEvent++;
10181107
10191108 // Fill TPC cluster histograms
0 commit comments