ATLAS Offline Software
Loading...
Searching...
No Matches
HIEventSelectionToolRun3.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
6#include <fstream>
7#include <nlohmann/json.hpp>
8
10
11std::string HI::toString(HI::IonDataType period) {
12#define ENUMDEF(_N) \
13 case HI::IonDataType::_N: \
14 return #_N;
15 switch (period) {
25 default:
26 return std::string("UNKNOWN HI DATA TAKING PERIOD ") +
27 std::to_string(static_cast<uint8_t>(period));
28 }
29#undef ENUMDEF
30}
31
32std::string HI::toString(HI::PileupVariation variation) {
33#define ENUMDEF(_N) \
34 case HI::PileupVariation::_N: \
35 return #_N;
36
37 switch (variation) {
41 default:
42 return std::string("UNKNOWN PU VARIATION ") +
43 std::to_string(static_cast<uint8_t>(variation));
44 }
45#undef ENUMDEF
46}
47
49#define ENUMDEF(_N) \
50 case HI::SelectionMask::_N: \
51 return #_N;
52
53 switch (m) {
63 default:
64 return std::string("UNKNOWN mask bit ") +
65 std::to_string(static_cast<unsigned int>(m));
66 }
67#undef ENUMDEF
68}
69
70static const double MeV = 1.;
71static const double GeV = 1e3;
72static const double TeV = 1e6;
73
74std::unique_ptr<TH1D> loadHist(const std::string& file) {
75 const std::string path =
76 PathResolver::find_file(std::string("HIEventUtils/") + file, "DATAPATH");
77 std::ifstream i(path);
78 if (not i) {
79 throw std::runtime_error(path + " does nto exist");
80 }
81
82 nlohmann::json j;
83 i >> j;
84 i.close();
85
86 const size_t nbins = j.at("fNbins").get<size_t>();
87
88 auto h = std::make_unique<TH1D>(j.at("fName").get<std::string>().c_str(),
89 j.at("fTitle").get<std::string>().c_str(),
90 nbins, j.at("fXmin").get<double>(),
91 j.at("fXmax").get<double>());
92 const std::vector<double> bins = j.at("fArray").get<std::vector<double>>();
93 if (bins.size() != j.at("fNbins").get<size_t>() + 2) {
94 throw std::runtime_error(
95 "Histogram " + j.at("fName").get<std::string>() +
96 " has inconsistent number of bins and fNbins (should +2) value " +
97 std::to_string(bins.size()) + " and " + std::to_string(nbins));
98 }
99
100 for (size_t bin = 0; bin < nbins + 2; bin++) {
101 h->SetBinContent(bin, bins[bin]);
102 }
103 h->SetDirectory(0);
104 return h;
105}
106
109
111 ATH_MSG_INFO("Initializing HIEventSelectionToolRun3");
112 // add printout of overall selection that is configured
113 if (!m_trackSelectionTool.empty())
115
116 //----------------------------------------------------------------------
117 // https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/
118 // also See Figure 2.6 of
119 // https://cds.cern.ch/record/2930965/files/ATL-COM-PHYS-2025-347.pdf
120 m_ZDCEt_UpperCut_5p5Sigma_OO = loadHist("PUFCalVsZDCNominalOO2025.json");
121
122 //----------------------------------------------------------------------
123
124 //----------------------------------------------------------------------
125 // https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/
126 // also See Figure 2.6 of
127 // https://cds.cern.ch/record/2930965/files/ATL-COM-PHYS-2025-347.pdf
128 m_ZDCEt_UpperCut_4p0Sigma_NeNe = loadHist("PUFCalVsZDCNominalNeNe2025.json");
129
130
131 //----------------------------------------------------------------------
132 // https://atlas-heavy-ions.docs.cern.ch/CorrFluc/centrality/
133 // https://cds.cern.ch/record/2921744 ATL-COM-PHYS-2025-033
134
135 m_ZDCEt_UpperCut_5Sigma_PbPb2023 = loadHist("PUFCalVsZDCNominalPbPb2023.json");
136
137 return StatusCode::SUCCESS;
138}
139
141 const xAOD::EventInfo* eventInfo) const {
142 if (!eventInfo)
143 return false;
144
145 // Standard ATLAS error checks
147 return false;
149 return false;
151 return false;
153 return false;
154
155 return true;
156}
157
160 float et = 0;
161 for (auto slice : *es) {
162 const static std::set fcalLayers({21, 22, 23});
163 if (fcalLayers.contains(slice->layer()))
164 et += slice->et();
165 }
166 return et * MeV / GeV; // we operate in GeV
167}
168
170 HI::IonDataType, const xAOD::ZdcModuleContainer* zdcModules) const {
171 float e = 0;
172 static const SG::ConstAccessor<float> calibEnergyAccessor("CalibEnergy");
173 for (auto module : *zdcModules) {
174 e += calibEnergyAccessor(*module);
175 }
176 return e * MeV / GeV; // we operate in GeV
177}
178
181 const xAOD::ZdcModuleContainer* zdcModules,
182 HI::PileupVariation variation) const {
183 return noPUZDCvsFCal(period, fcalEt(period, es), zdcE(period, zdcModules),
184 variation);
185}
186
188 HI::IonDataType period, float fcalEt, float zdcE,
189 HI::PileupVariation variation) const {
190 const float cut = zdcCutValue(period, fcalEt, variation);
191 return zdcE < cut;
192}
193
196 const xAOD::VertexContainer* vertices) const {
197 const xAOD::Vertex* pv = 0;
198 for (const xAOD::Vertex* vx : *vertices) {
199 if (vx->vertexType() == xAOD::VxType::PriVtx) {
200 pv = vx;
201 break;
202 }
203 }
204
205 int count = 0;
206 for (const xAOD::TrackParticle* trk : *tracks) {
207 if (m_trackSelectionTool->accept(*trk, pv)) {
208 count++;
209 }
210 }
211 return count;
212}
213
216 const xAOD::TrackParticleContainer* tracks,
217 const xAOD::VertexContainer* vertices, PileupVariation variation) const {
218 return noPUFCalVsNtracks(period, fcalEt(period, es),
219 nTrk(period, tracks, vertices), variation);
220}
221
223 HI::IonDataType period, float fcalEt, int ntrk, HI::PileupVariation) const {
224 ATH_MSG_DEBUG("cutting puFCalVsNtracks: fcalEt " << fcalEt << " ntracks "
225 << ntrk);
226 // for reference, thes numbers are taken from:
227 // https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/#fcal-sumet-ntrk-correlation-cut
228 // and more is here:
229 // https://cds.cern.ch/record/2930965/files/ATL-COM-PHYS-2025-347.pdf
230 if (period == HI::IonDataType::OO2025) {
231 if (ntrk < (-80 + fcalEt * 0.6))
232 return false;
233 if (ntrk < (-30 + fcalEt * 0.4))
234 return false;
235 if (ntrk > (100 + fcalEt * 1.7))
236 return false;
237 return true;
238 } else if (period == HI::IonDataType::NeNe2025) {
239 if (ntrk < (-70 + fcalEt * 0.6))
240 return false;
241 if (ntrk < (-20 + fcalEt * 0.35))
242 return false;
243 if (ntrk > (100 + fcalEt * 1.7))
244 return false;
245 return true;
246 }
247 throw std::runtime_error(std::string("puFCalVsNtracks for period of id ") +
248 HI::toString(period) + " is not handled (yet)");
249
250 return false; // for unimplemented periods
251}
252
254 const xAOD::ZdcModuleContainer* zdcModules) const {
255 float PreSamplerAmp_A = 0;
256 float PreSamplerAmp_C = 0;
257 static const SG::ConstAccessor<float> accPreSampleAmp("PreSampleAmp");
258 for (const auto module : *zdcModules) {
259
260 if (module->zdcType() != 0)
261 continue;
262 if (module->zdcSide() > 0)
263 PreSamplerAmp_C += accPreSampleAmp(*module);
264 if (module->zdcSide() < 0)
265 PreSamplerAmp_A += accPreSampleAmp(*module);
266 }
267 return {PreSamplerAmp_A, PreSamplerAmp_C};
268}
269
271 HI::IonDataType period, const xAOD::ZdcModuleContainer* zdcModules,
272 HI::PileupVariation variation) const {
273 auto [PreSamplerAmp_A, PreSamplerAmp_C] = ZDCPresamplerAmps(zdcModules);
274 return noPUZDCPresampler(period, PreSamplerAmp_A, PreSamplerAmp_C, variation);
275}
276
278 HI::IonDataType period, float presamplerA, float presamplerC,
279 HI::PileupVariation variation) const {
280 if (period == HI::IonDataType::PbPb2023) {
281 // from ATL-COM-PHYS-2025-033 + priv. communication F.Pauwels
282 const float peakPositionA = -56;
283 const float peakPositionC = -156;
284 const float peakWidthA = 51.8;
285 const float peakWidthC = 51.8;
286 float sigma = 7;
287 if (variation == HI::PileupVariation::Tight) {
288 sigma = 8;
289 }
290 if (variation == HI::PileupVariation::Loose) {
291 sigma = 6;
292 }
293 if (presamplerA > (peakPositionA + sigma * peakWidthA) and
294 presamplerC > (peakPositionC + sigma * peakWidthC)) {
295 return false; // it is pileup
296 }
297 return true;
298 }
299 throw std::runtime_error(std::string("ZDC PreSampleAmp for period of id ") +
300 HI::toString(period) + " is not handled (yet)");
301}
302
304 HI::IonDataType, const xAOD::VertexContainer* vertices) const {
305
306 // This is probably redundant as the vx->vertexType() should not be
307 // xAOD::VxType::PriVtx for the dummy vertex
308 if (vertices->size() <= 1) {
309 ATH_MSG_DEBUG("Only dummy vertex present, returning false");
310 return false;
311 }
312
313 unsigned int nPrimary = 0;
314 unsigned int nSplit = 0;
315 unsigned int nOther = 0;
316 // count primary vertices with sigma_z^2 < threshold
317 // documentation: https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/
318 for (const xAOD::Vertex* vx : *vertices) {
319 if (vx->vertexType() == xAOD::VxType::PriVtx) {
320 // check Primary vertices to see if there are some of good quality
321 AmgSymMatrix(3) vtx_err = vx->covariancePosition();
322 const double sigmaZSq = vtx_err(2, 2);
323 if (sigmaZSq >= 0.02) // cut in mm^2
324 ++nSplit;
325 else
326 ++nPrimary;
327 }
328 // vertices that are not PV, note that dummy vertex probably will get
329 // assigned here
330 else
331 ++nOther;
332 }
333 ATH_MSG_DEBUG("n primary " << nPrimary << ", nSplit " << nSplit
334 << ", nOther " << nOther);
335
336 // If all vertices were classified as split, then we consider one of them to
337 // be a real vertex
338 if (nSplit > 0 && nPrimary == 0) {
339 ATH_MSG_DEBUG("Returning true as all vertices classified as split");
340 return true;
341 }
342
343 return nPrimary == 1;
344}
345
347 HI::IonDataType period, float fcalEt, HI::PileupVariation variation) const {
348
349 if (period == HI::IonDataType::PbPb2023) {
350 // the cut is expressed in TeV for 23 data
351 float fcalEtTeV = fcalEt * GeV / TeV;
352
353 // float cut = cutFunction(fcalEtTeV);
354 if (fcalEtTeV <= 1.0) // below 1 TeV use value at 1 TeV
355 fcalEtTeV = 1.0;
356 if (fcalEtTeV >= 4.0) // above4 TeV use value at 4 TeV
357 fcalEtTeV = 1.0;
358
359 int refbin = m_ZDCEt_UpperCut_5Sigma_PbPb2023->FindFixBin(fcalEtTeV);
360 float cut = m_ZDCEt_UpperCut_5Sigma_PbPb2023->GetBinContent(refbin);
361
362 if (variation == HI::PileupVariation::Tight) {
363 cut *= 1.02;
364 }
365 if (variation == HI::PileupVariation::Loose) {
366 cut *= 0.98;
367 }
368 return cut;
369 }
370
371 // https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/
372 // also See Figure 2.6 of
373 // https://cds.cern.ch/record/2930965/files/ATL-COM-PHYS-2025-347.pdf
374 if (period == HI::IonDataType::OO2025) {
375 int refbin = m_ZDCEt_UpperCut_5p5Sigma_OO->FindFixBin(fcalEt);
376 if (refbin < 1)
377 refbin = 1;
378
379 return m_ZDCEt_UpperCut_5p5Sigma_OO->GetBinContent(refbin);
380 }
381
382 // https://atlas-heavy-ions.docs.cern.ch/analyzes/2025/
383 // also See Figure 2.6 of
384 // https://cds.cern.ch/record/2930965/files/ATL-COM-PHYS-2025-347.pdf
385 if (period == HI::IonDataType::NeNe2025) {
386 int refbin = m_ZDCEt_UpperCut_4p0Sigma_NeNe->FindFixBin(fcalEt);
387 if (refbin < 1)
388 refbin = 1;
389
390 return m_ZDCEt_UpperCut_4p0Sigma_NeNe->GetBinContent(refbin);
391 }
392
393 throw std::runtime_error(
394 std::string("zdcCutValue needed in FCal vs ZDC E for period of id ") +
395 HI::toString(period) + " is not handled (yet)");
396
397 return 0;
398}
399
404
409
411 uint32_t run) const {
412 if (run < 365498)
413 throw std::runtime_error(std::to_string(run) +
414 " not handled by selection tool");
415 if (run <= 367384)
417 if (run <= 463427)
419 if (run <= 489691)
421 if (run <= 490223)
423 if (run <= 501969)
425 if (run <= 502008)
427 if (run <= 512049)
429 // fill it up with 2026
430 throw std::runtime_error(std::to_string(run) +
431 " not handled by selection tool");
432}
433
435 HI::IonDataType period) const {
436 if (period == HI::IonDataType::OO2025 or
437 period == HI::IonDataType::NeNe2025) {
438 return static_cast<unsigned int>(HI::SelectionMask::OODefault);
439 }
440 // this will likely evolve
441 return static_cast<unsigned int>(HI::SelectionMask::PBDefault);
442}
443
444// } // namespace HI
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
#define AmgSymMatrix(dim)
static const double TeV
#define ENUMDEF(_N)
static const double MeV
std::unique_ptr< TH1D > loadHist(const std::string &file)
static const std::vector< std::string > bins
Header file for AthHistogramAlgorithm.
size_type size() const noexcept
Returns the number of elements in the collection.
virtual int nTrk(HI::IonDataType dataType, const xAOD::TrackParticleContainer *tracks, const xAOD::VertexContainer *vertices) const override
virtual float zdcE(HI::IonDataType period, const xAOD::ZdcModuleContainer *zdcModules) const override
virtual std::pair< float, float > ZDCPresamplerAmps(const xAOD::ZdcModuleContainer *zdcModules) const override
obtain presampler amplitudes
virtual StatusCode initialize() override
Dummy implementation of the initialisation function.
virtual float fcalEt(HI::IonDataType period, const xAOD::HIEventShapeContainer *es) const override
float zdcCutValue(IonDataType, float fcalEt, PileupVariation) const
ToolHandle< InDet::IInDetTrackSelectionTool > m_trackSelectionTool
virtual unsigned int defaultMaskForPeriod(IonDataType period) const override
provides default set of cuts for given period
std::unique_ptr< TH1D > m_ZDCEt_UpperCut_4p0Sigma_NeNe
virtual bool noPUOOVertexCuts(IonDataType dataType, const xAOD::VertexContainer *vertices) const override
true if this is NOT pileup event
virtual bool noDetectorError(const xAOD::EventInfo *eventInfo) const override
Checks basic event flags.
float ntrkCutValue(IonDataType, float fcalEt, PileupVariation) const
virtual bool noPUFCalVsNtracks(IonDataType dataType, const xAOD::HIEventShapeContainer *es, const xAOD::TrackParticleContainer *tracks, const xAOD::VertexContainer *vertices, PileupVariation variation=PileupVariation::Nominal) const override
true if this is NOT pileup event The fool performs track selection
virtual bool noPUZDCvsFCal(HI::IonDataType period, const xAOD::HIEventShapeContainer *es, const xAOD::ZdcModuleContainer *zdcModules, HI::PileupVariation variation) const override
true if this is NOT pileup event It computes necessary quantities and invokes method defined next to ...
HIEventSelectionToolRun3(const std::string &name)
std::unique_ptr< TH1D > m_ZDCEt_UpperCut_5p5Sigma_OO
virtual IonDataType toDataType(const xAOD::EventInfo *eventInfo) const override
translates info in EV into HI data type
IonDataType runNumberToDataType(uint32_t run) const
std::unique_ptr< TH1D > m_ZDCEt_UpperCut_5Sigma_PbPb2023
virtual bool noPUZDCPresampler(HI::IonDataType period, const xAOD::ZdcModuleContainer *zdcModules, HI::PileupVariation variation) const override
true if this is NOT pileup event
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
Helper class to provide constant type-safe access to aux data.
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
@ Tile
The Tile calorimeter.
@ Pixel
The pixel tracker.
@ LAr
The LAr calorimeter.
@ Error
The sub-detector issued an error.
uint32_t runNumber() const
The current event's run number.
EventFlagErrorState errorState(EventFlagSubDet subDet) const
Get the error state for a particular sub-detector.
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
std::string toString(IonDataType)
@ PriVtx
Primary vertex.
ZdcModuleContainer_v1 ZdcModuleContainer
EventInfo_v1 EventInfo
Definition of the latest event info version.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
VertexContainer_v1 VertexContainer
Definition of the current "Vertex container version".
Vertex_v1 Vertex
Define the latest version of the vertex class.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
HIEventShapeContainer_v2 HIEventShapeContainer
Define the latest version of the container.
Extra patterns decribing particle interation process.
TFile * file
int run(int argc, char *argv[])