ATLAS Offline Software
Loading...
Searching...
No Matches
BjetHitZConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2"""Hit-based per-jet z regression (HitZ) for the b-jet trigger.
3
4Runs, in a step of its own over the jet super-RoI and before any tracking,
5the hit pipeline the training-dataset-dumper runs offline: the Si clusters are
6built in the view, converted to xAOD, cleaned, decorated with beamspot-relative
7coordinates, associated to the jets in a wedge, merged and truncated, then
8passed to an ONNX regression.
9
10Unlike the taggers in BjetFlavourTaggingConfig, nothing here consumes tracks or
11a vertex.
12"""
13
14from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
15from AthenaConfiguration.ComponentFactory import CompFactory
16
17# xAOD containers the cluster converters write into
18PIXEL_HITS = 'PixelClusters'
19STRIP_HITS = 'StripClusters'
20
21# HitsLoader in FlavorTagInference resolves the hits by this decoration name and
22# does not honour variableRemapping, so it is not free to choose
23HIT_ASSOCIATION = 'hitsAssociatedWithJet'
24
25# PixelPrepDataToxAOD writes these through the AUXDATA macro rather than a
26# WriteDecorHandleKey, so the scheduler cannot see them without a nudge
27PIXEL_GOODNESS_DECORS = ['isFake', 'hasBSError', 'DCSState']
28
29# The wedge and the hit budget have to match what the network was trained with
30MAX_HITS = 700
31DPHI_HIT_TO_JET = 0.1
32DETA_HIT_TO_JET = 0.1
33DZ_HIT_TO_VERTEX = 180
34
35# HitZ runs before the menu's JetViewAlg, so it applies its own acceptance
36MIN_JET_PT = 20e3
37MAX_ABS_JET_ETA = 4.0
38
39# Map the MDN output names onto the (z, sigma) pair the menu side expects
40Z_DECOR = 'HitZ_z0'
41SIGMA_DECOR = 'HitZ_z0_sigma'
42
43OUTPUT_REMAP = {
44 'HitZV01_TruthJetPVz': Z_DECOR,
45 'HitZV01_TruthJetPVz_stddev': SIGMA_DECOR,
46}
47
48
50 """xAOD converters for the in-view Si clusters."""
51 ca = ComponentAccumulator()
52
53 from InDetConfig.InDetPrepRawDataToxAODConfig import (
54 ITkPixelPrepDataToxAODCfg,
55 ITkStripPrepDataToxAODCfg,
56 )
57 # Both converters write far more than HitZ reads. All the pipeline needs is
58 # the global position (the network eats beamspot-relative coordinates built
59 # from it), bec (the barrel/endcap split in the association) and, on the
60 # pixel side, the three goodness flags. The rest is switched off:
61 # - WriteRDOinformation pulls the per-RDO charge calibration on the pixel
62 # side and builds an Identifier->RDO map over the whole strip RDO
63 # container on the strip side, once per event.
64 # - WriteExtendedPRDinformation adds four module-geometry lookups per
65 # cluster.
66 # WriteModuleStatus keeps hasBSError/DCSState, which used to be reachable
67 # only through WriteRDOinformation.
68 pixelSlimming = {}
69 stripSlimming = {}
70 if flags.Trigger.Jet.hitZSlimConverters:
71 pixelSlimming = dict(WriteRDOinformation=False,
72 WriteExtendedPRDinformation=False,
73 WriteModuleStatus=True)
74 stripSlimming = dict(WriteRDOinformation=False)
75
76 ca.merge(ITkPixelPrepDataToxAODCfg(
77 flags,
78 SiClusterContainer='ITkTrigPixelClusters',
79 OutputClusterContainer=PIXEL_HITS,
80 WriteNNinformation=False,
81 UseTruthInfo=False,
82 **pixelSlimming,
83 ))
84 ca.merge(ITkStripPrepDataToxAODCfg(
85 flags,
86 SiClusterContainer='ITkTrigStripClusters',
87 # SCT_PrepDataToxAOD spells its xAOD output differently to the pixel one
88 SctxAodContainer=STRIP_HITS,
89 UseTruthInfo=False,
90 **stripSlimming,
91 ))
92
93 ca.getEventAlgo('ITkPixelPrepDataToxAOD').ExtraOutputs = [
94 ('xAOD::TrackMeasurementValidationContainer',
95 f'StoreGateSvc+{PIXEL_HITS}.{decor}')
96 for decor in PIXEL_GOODNESS_DECORS
97 ]
98 return ca
99
100
102 """The merged association, as the scheduler has to be told to see it."""
103 return ('xAOD::BaseContainer',
104 f'StoreGateSvc+{inputJets}.{HIT_ASSOCIATION}')
105
106
107def hitAssociationCfg(flags, inputJets, maxHits=MAX_HITS,
108 dphi=DPHI_HIT_TO_JET, deta=DETA_HIT_TO_JET,
109 dz=DZ_HIT_TO_VERTEX, minJetPt=MIN_JET_PT,
110 maxAbsJetEta=MAX_ABS_JET_ETA):
111 """Clean, decorate and associate the Pixel and Strip hits to the jets."""
112 ca = ComponentAccumulator()
113
114 from BeamSpotConditions.BeamSpotConditionsConfig import BeamSpotCondAlgCfg
115 ca.merge(BeamSpotCondAlgCfg(flags))
116
117 for label, hits, isSCT in [('Pixel', PIXEL_HITS, False),
118 ('Strip', STRIP_HITS, True)]:
119 ca.addEventAlgo(
120 CompFactory.FlavorTagDiscriminants.CleanHitDecoratorAlg(
121 name=f'HitZCleanHit{label}',
122 hitContainer=hits,
123 isSCT=isSCT,
124 DoOverlapRemoval=False,
125 ))
126 ca.addEventAlgo(
127 CompFactory.FlavorTagDiscriminants.HitBeamSpotDataDecoratorAlg(
128 name=f'HitZBeamSpotDeco{label}',
129 hitContainer=hits,
130 beamSpotKey='BeamSpotData',
131 ))
132 ca.addEventAlgo(
133 CompFactory.FlavorTagDiscriminants.JetHitAssociationAlg(
134 name=f'HitZJetHitAssociation{label}',
135 jetContainer=inputJets,
136 hitContainer=hits,
137 hitAssociation=f'{HIT_ASSOCIATION}{"SCT" if isSCT else "Pixel"}',
138 dphiHitToJet=dphi,
139 detaHitToJet=deta,
140 dzHitToVertex=dz,
141 minJetPt=minJetPt,
142 maxAbsJetEta=maxAbsJetEta,
143 # truncation happens downstream, on the merged collection
144 maxHits=0,
145 ))
146
147 ca.addEventAlgo(
148 CompFactory.FlavorTagDiscriminants.HitsSelectorAlg(
149 name='HitZHitsSelector',
150 jetContainer=inputJets,
151 # declared against jetContainer as parent, so no container prefix
152 PixelAssociation=f'{HIT_ASSOCIATION}Pixel',
153 SCTAssociation=f'{HIT_ASSOCIATION}SCT',
154 hitAssociation=HIT_ASSOCIATION,
155 pixelHitContainer=PIXEL_HITS,
156 SCTHitContainer=STRIP_HITS,
157 maxHits=maxHits,
158 # HitsLoader reads this back through a bare ConstAccessor, so the
159 # scheduler needs the edge to the tagger drawn by hand
160 ExtraOutputs=[hitAssociationDataObjID(inputJets)],
161 ))
162 return ca
163
164
165def hitZInferenceCfg(flags, inputJets, nnFile, remap=None):
166 """Run the HitZ regression, mirroring the dl2_configs entries next door."""
167 ca = ComponentAccumulator()
168
169 # GNNTool builds its own CPU session unless it is given a sharing service;
170 # the service is the only place the execution provider can be chosen
171 nnSvc = CompFactory.FlavorTagInference.NNSharingOnnxSvc(
172 'HitZNNSharingOnnxSvc',
173 executionProvider=flags.Trigger.Jet.hitZExecutionProvider,
174 deviceId=flags.Trigger.Jet.hitZDeviceId,
175 useTF32=flags.Trigger.Jet.hitZUseTF32,
176 )
177 ca.addService(nnSvc)
178
179 ca.addEventAlgo(
180 CompFactory.FlavorTagInference.JetTagDecoratorAlg(
181 name='HitZJetTagAlg',
182 container=inputJets,
183 electronContainer='',
184 # the network reads hits, not tracks, so it declares no track
185 # inputs and needs no constituent container
186 constituentContainer='',
187 decorator=CompFactory.FlavorTagInference.GNNTool(
188 name='HitZGNNTool',
189 nnFile=nnFile,
190 variableRemapping=OUTPUT_REMAP if remap is None else remap,
191 defaultZeroTracks=True,
192 nnSharingService=nnSvc,
193 ),
194 ExtraInputs=[hitAssociationDataObjID(inputJets)],
195 ))
196 return ca
197
198
199def hitZTaggingCfg(flags, inputJets):
200 """Cluster conversion, hit association and HitZ inference, in that order."""
201 if not flags.Detector.GeometryITk:
202 raise ValueError('HitZ is configured for ITk (Run 4) only')
203 if flags.Trigger.useActsTracking:
204 raise ValueError('HitZ reads the InDet trigger clusters, which '
205 'Trigger.useActsTracking does not produce')
206
207 nnFile = flags.Trigger.Jet.hitZNetwork
208 if not nnFile:
209 raise ValueError(
210 'An HZ chain is configured but Trigger.Jet.hitZNetwork is empty. '
211 'Point it at a HitZ ONNX file resolvable by PathResolver.')
212
213 ca = ComponentAccumulator()
214 ca.merge(hitClusterCnvCfg(flags))
215 ca.merge(hitAssociationCfg(flags, inputJets))
216 ca.merge(hitZInferenceCfg(flags, inputJets, nnFile))
217 return ca
218
219
220def hitZRoICfg(flags, inputJets, RoIs):
221 """Everything that runs in the HitZ super-RoI view: Si clusterisation, then HitZ."""
222 ca = ComponentAccumulator()
223
224 # the clusterisation algs carry the signature in their name, so a
225 # signature of its own keeps them apart from the RoI FTF step's
226 signature = 'jetSuperHitZ'
227 from AthenaCommon.Logging import logging
228 from TrigInDetConfig.utils import getFlagsForActiveConfig
229 from TrigInDetConfig.ITkTrigSequence import ITkTrigSequence
230 trkFlags = getFlagsForActiveConfig(flags, 'jetSuper', logging.getLogger(__name__))
231 ca.merge(ITkTrigSequence(trkFlags, signature, RoIs, 'VDVHitZ')
232 .viewDataVerifier('VDVHitZ'))
233 ca.addEventAlgo(CompFactory.AthViews.ViewDataVerifier(
234 name='VDVHitZJets',
235 DataObjects={('xAOD::JetContainer', f'StoreGateSvc+{inputJets}')},
236 ))
237
238 from InDetConfig.InDetPrepRawDataFormationConfig import (
239 ITkTrigPixelClusterizationCfg,
240 ITkTrigStripClusterizationCfg,
241 )
242 ca.merge(ITkTrigPixelClusterizationCfg(trkFlags, roisKey=RoIs, signature=signature))
243 ca.merge(ITkTrigStripClusterizationCfg(trkFlags, roisKey=RoIs, signature=signature))
244
245 ca.merge(hitZTaggingCfg(flags, inputJets))
246 return ca
hitZInferenceCfg(flags, inputJets, nnFile, remap=None)
hitZRoICfg(flags, inputJets, RoIs)
hitAssociationCfg(flags, inputJets, maxHits=MAX_HITS, dphi=DPHI_HIT_TO_JET, deta=DETA_HIT_TO_JET, dz=DZ_HIT_TO_VERTEX, minJetPt=MIN_JET_PT, maxAbsJetEta=MAX_ABS_JET_ETA)