HLTTriggerResultGetter.py 20 KB
Newer Older
1
# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
2
3

from TriggerJobOpts.TriggerFlags import TriggerFlags
4
from AthenaConfiguration.AllConfigFlags import ConfigFlags
5
6
from AthenaCommon.Logging import logging
from AthenaCommon.GlobalFlags import globalflags
7

8
from AthenaCommon.AppMgr import ServiceMgr
9
10
11
12
from RecExConfig.Configured import Configured

from RecExConfig.RecFlags import rec

13
14
from TrigRoiConversion.TrigRoiConversionConf import RoiWriter

15
16
17

class xAODConversionGetter(Configured):
    def configure(self):
18
        from AthenaCommon.AlgSequence import AlgSequence
19
20
21
22
23
24
25
26
        topSequence = AlgSequence()

        #schedule xAOD conversions here
        from TrigBSExtraction.TrigBSExtractionConf import TrigHLTtoxAODConversion
        xaodconverter = TrigHLTtoxAODConversion()
        
        from TrigNavigation.TrigNavigationConfig import HLTNavigationOffline
        xaodconverter.Navigation = HLTNavigationOffline()
27

28
29
        from TrigEDMConfig.TriggerEDM import getPreregistrationList
        from TrigEDMConfig.TriggerEDM import getEFRun1BSList,getEFRun2EquivalentList,getL2Run1BSList,getL2Run2EquivalentList
30
        xaodconverter.Navigation.ClassesToPreregister = getPreregistrationList(ConfigFlags.Trigger.EDMVersion)
31

32
33
34
        # we want only containers from Run 1 with the BS tag
        xaodconverter.BStoxAOD.ContainersToConvert = getL2Run1BSList() + getEFRun1BSList()
        xaodconverter.BStoxAOD.NewContainers = getL2Run2EquivalentList() + getEFRun2EquivalentList()
35
36
37
38

        xaodconverter.HLTResultKey="HLTResult_EF"
        topSequence += xaodconverter

39
40
41
42
43
44
        # define list of HLT xAOD containers to be written to the output root file
        # (previously this was defined in HLTTriggerResultGetter def configure)
        from TrigEDMConfig.TriggerEDM import getTriggerEDMList
        self.xaodlist = {}
        self.xaodlist.update( getTriggerEDMList(TriggerFlags.ESDEDMSet(), 2 ))

45
46
47
48
49
50
        return True
    
        

class ByteStreamUnpackGetter(Configured):
    def configure(self):
51
        log = logging.getLogger("ByteStreamUnpackGetter")
52

53
54
55
56
57
58
59
60
61
62
63
64
        log.info( "TriggerFlags.dataTakingConditions: %s", TriggerFlags.dataTakingConditions() )
        hasHLT = TriggerFlags.dataTakingConditions()=='HltOnly' or TriggerFlags.dataTakingConditions()=='FullTrigger'
        if not hasHLT:
            log.info("Will not configure HLT BS unpacking because dataTakingConditions flag indicates HLT was disabled")
            return True

        # Define the decoding sequence
        from TrigHLTResultByteStream.TrigHLTResultByteStreamConf import HLTResultMTByteStreamDecoderAlg
        from TrigOutputHandling.TrigOutputHandlingConf import TriggerEDMDeserialiserAlg
        from AthenaCommon.CFElements import seqAND
        decoder = HLTResultMTByteStreamDecoderAlg()
        deserialiser = TriggerEDMDeserialiserAlg("TrigDeserialiser")
65
        deserialiser.ExtraOutputs += [('xAOD::TrigCompositeContainer' , 'StoreGateSvc+HLTNav_Summary_OnlineSlimmed')]
66
67
68
69
70
71
72
73
74
75
76
77
        decodingSeq = seqAND("HLTDecodingSeq")
        decodingSeq += decoder  # BS -> HLTResultMT
        decodingSeq += deserialiser  # HLTResultMT -> xAOD

        # Append the decoding sequence to topSequence
        from AthenaCommon.AlgSequence import AlgSequence
        topSequence = AlgSequence()
        topSequence += decodingSeq

        log.debug("Configured HLT result BS decoding sequence")
        return True

78
class ByteStreamUnpackGetterRun1or2(Configured):
79
80
    def configure(self):

81
        log = logging.getLogger("ByteStreamUnpackGetterRun1or2")
82
83
84
85
        from AthenaCommon.AlgSequence import AlgSequence 
        topSequence = AlgSequence()
        
        #if TriggerFlags.readBS():
86
        log.info( "TriggerFlags.dataTakingConditions: %s", TriggerFlags.dataTakingConditions() )
87
        # in MC this is always FullTrigger
88
        hasHLT = TriggerFlags.dataTakingConditions() in ('HltOnly', 'FullTrigger')
89
90
91
92
        
        # BS unpacking
        from TrigBSExtraction.TrigBSExtractionConf import TrigBSExtraction
        extr = TrigBSExtraction()
93
94

        # Add fictional output to ensure data dependency in AthenaMT
Tim Martin's avatar
Tim Martin committed
95
96
        # Keeping the run 2 workflow, we run this after we have put the full serialised navigation into xAOD
        extr.ExtraInputs += [("xAOD::TrigNavigation", "StoreGateSvc+TrigNavigation")]
97
        extr.ExtraOutputs += [("TrigBSExtractionOutput", "StoreGateSvc+TrigBSExtractionOutput")]
98
99
100
        
        if hasHLT:
            from TrigNavigation.TrigNavigationConfig import HLTNavigationOffline
101
102
103
104
            extr.NavigationForL2 = HLTNavigationOffline("NavigationForL2")
            # Ignore the L2 TrigPassBits to avoid clash with EF (ATR-23411)
            extr.NavigationForL2.ClassesFromPayloadIgnore = ["TrigPassBits#passbits"]

105
            extr.Navigation = HLTNavigationOffline()
106
107

            from TrigEDMConfig.TriggerEDM import getEDMLibraries
108
            extr.Navigation.Dlls = getEDMLibraries()            
109
110

            from TrigEDMConfig.TriggerEDM import getPreregistrationList
111
            extr.Navigation.ClassesToPreregister = getPreregistrationList(ConfigFlags.Trigger.EDMVersion)
112

113
114
            from eformat import helper as efh
            robIDMap = {}   # map of result keys and their ROB ID
115
            if ConfigFlags.Trigger.EDMVersion == 1:  # Run-1 has L2 and EF result
116
117
                robIDMap["HLTResult_L2"] = efh.SourceIdentifier(efh.SubDetector.TDAQ_LVL2, 0).code()
                robIDMap["HLTResult_EF"] = efh.SourceIdentifier(efh.SubDetector.TDAQ_EVENT_FILTER, 0).code()
118
                extr.L2ResultKey = "HLTResult_L2"
119
                extr.HLTResultKey = "HLTResult_EF"
120
            else:
121
                robIDMap["HLTResult_HLT"] = efh.SourceIdentifier(efh.SubDetector.TDAQ_HLT, 0).code()
122
123
                extr.L2ResultKey = ""
                extr.HLTResultKey = "HLTResult_HLT"
124
125

            # Configure DataScouting
126
            from PyUtils.MetaReaderPeeker import metadata
127
            if 'stream' in metadata:
128
                stream_local = metadata['stream']   # e.g. calibration_DataScouting_05_Jets
129
                if stream_local.startswith('calibration_DataScouting_'):
130
                    ds_tag = '_'.join(stream_local.split('_')[1:3])   # e.g. DataScouting_05
131
132
133
                    ds_id = int(stream_local.split('_')[2])           # e.g. 05
                    robIDMap[ds_tag] = efh.SourceIdentifier(efh.SubDetector.TDAQ_HLT, ds_id).code()
                    extr.DSResultKeys += [ds_tag]
134
135

        else:
136
137
138
139
140
            # if data doesn't have HLT info set HLTResult keys as empty strings to avoid warnings
            # but the extraction algorithm must run
            extr.L2ResultKey = ""
            extr.HLTResultKey = ""
            extr.DSResultKeys = []
141
142
143

        topSequence += extr

144
145
146
147
148
        # Add all HLTResult keys to AddressProvider
        for k in robIDMap.keys():
            ServiceMgr.ByteStreamAddressProviderSvc.TypeNames += [ f"HLT::HLTResult/{k}" ]

        # Create necessary public tools
149
        from AthenaCommon.AppMgr import ToolSvc
150
        from TrigSerializeTP.TrigSerializeTPConf import TrigSerTPTool
151
        from TrigEDMConfig.TriggerEDM import getTPList
152
        ToolSvc += TrigSerTPTool(TPMap = getTPList((ConfigFlags.Trigger.EDMVersion)))
153
154
        
        from TrigSerializeCnvSvc.TrigSerializeCnvSvcConf import TrigSerializeConvHelper
155
156
157
158
        ToolSvc += TrigSerializeConvHelper(doTP = True)

        from TrigHLTResultByteStream.TrigHLTResultByteStreamConf import HLT__HLTResultByteStreamTool
        ToolSvc += HLT__HLTResultByteStreamTool(HLTResultRobIdMap = robIDMap)
159
160
161
162
163

        return True


class TrigDecisionGetter(Configured):
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    def configure(self):
        log = logging.getLogger("TrigDecisionGetter")

        from AthenaCommon.AlgSequence import AlgSequence
        topSequence = AlgSequence()

        from TrigDecisionMaker.TrigDecisionMakerConfig import TrigDecisionMakerMT
        tdm = TrigDecisionMakerMT('TrigDecMakerMT')

        if not TriggerFlags.readBS():
            # Construct trigger bits from HLTNav_summary instead of reading from BS
            from TrigOutputHandling.TrigOutputHandlingConf import TriggerBitsMakerTool
            tdm.BitsMakerTool = TriggerBitsMakerTool()

        topSequence += tdm
        log.info('xTrigDecision writing enabled')

        return True

183
class TrigDecisionGetterRun1or2(Configured):
184
185
186
    #class to setup the writing or just making of TrigDecisionObject
    def configure(self):
        
187
        log = logging.getLogger("TrigDecisionGetterRun1or2")
188
189
190
191
192
193
194
195
196
197
198

        from AthenaCommon.AlgSequence import AlgSequence 
        topSequence = AlgSequence()
        
        #if hasOnlyLVL1:
        #from RecExConfig.ObjKeyStore import objKeyStore
        #objKeyStore.addStreamESD('TrigDec::TrigDecision','TrigDecision')
        #objKeyStore.addStreamAOD('TrigDec::TrigDecision','TrigDecision')
        
        from RecExConfig.RecFlags import rec
        if ( rec.doWriteESD() or rec.doWriteAOD() or rec.doESD() or rec.doAOD() ) and \
199
               ( not ( rec.readAOD() or rec.readESD() or rec.doWriteBS()) ):
200
            log.info("Will write TrigDecision object to storegate")
201
            
202
            from TrigDecisionMaker.TrigDecisionMakerConfig import WriteTrigDecision
203
            trigDecWriter = WriteTrigDecision()  # noqa: F841
204
205
206
            if (ConfigFlags.Trigger.EDMVersion == 1 or ConfigFlags.Trigger.EDMVersion == 2) and ConfigFlags.Trigger.doEDMVersionConversion:
                from TrigNavTools.NavConverterConfig import createNavConverterAlg
                navCnvAlg = createNavConverterAlg()
207
                navCnvAlg.TrigConfigSvc = "HLTConfigSvcRun3"
208
209
                navCnvAlg.ExtraInputs += [("TrigBSExtractionOutput", "StoreGateSvc+TrigBSExtractionOutput")]
                topSequence += navCnvAlg
210

211
#           WritexAODTrigDecision() is called within WriteTrigDecision()
212

213
214
215
216
217
            # inform TD maker that some parts may be missing
            if TriggerFlags.dataTakingConditions()=='Lvl1Only':
                topSequence.TrigDecMaker.doL2=False
                topSequence.TrigDecMaker.doEF=False
                topSequence.TrigDecMaker.doHLT=False
218
219
220
                topSequence.TrigNavigationCnvAlg.doL2 = False
                topSequence.TrigNavigationCnvAlg.doEF = False
                topSequence.TrigNavigationCnvAlg.doHLT = False
221
222
223
            elif TriggerFlags.dataTakingConditions()=='HltOnly':
                from AthenaCommon.AlgSequence import AlgSequence
                topSequence.TrigDecMaker.doL1=False
224
225

            if ConfigFlags.Trigger.EDMVersion == 1:  # Run-1 has L2 and EF result
226
                topSequence.TrigDecMaker.doHLT = False
227
228
                topSequence.TrigNavigationCnvAlg.doL2 = False
                topSequence.TrigNavigationCnvAlg.doHLT = False
229
230
231
            else:
                topSequence.TrigDecMaker.doL2 = False
                topSequence.TrigDecMaker.doEF = False
232
233
                topSequence.TrigNavigationCnvAlg.doL2 = False
                topSequence.TrigNavigationCnvAlg.doEF = False
234
235
236
237
                pass
                
        else:
            log.info("Will not write TrigDecision object to storegate")
238
    
239
240
241
242
243
244
245
246
247
248
249
250
        return True
    
    
class HLTTriggerResultGetter(Configured):

    log = logging.getLogger("HLTTriggerResultGetter.py")

    def _AddOPIToESD(self):

        log = logging.getLogger("HLTTriggerResultGetter.py")        
        
        if rec.doESD():
251
252
253
            from PyUtils.MetaReaderPeeker import metadata
            if 'stream' in metadata:
                stream = metadata['stream']
254
                log.debug("the stream found in 'metadata' is %s", stream)
255
256
                if "express" in stream:
                    from TrigEDMConfig.TriggerEDM import getTypeAndKey,EDMDetails
257
                    type,key=getTypeAndKey("TrigOperationalInfo#HLT_EXPRESS_OPI_HLT")
258
                    if 'collection'in EDMDetails[type]:
259
                        colltype = EDMDetails[type]['collection']
260
                        log.info("Adding HLT_EXPRESS_OPI_HLT to ESD for stream %s", stream)
261
262
263
264
                        from RecExConfig.ObjKeyStore import objKeyStore
                        objKeyStore.addStreamESD(colltype, key)
                    return True
            else:
265
                log.warning("Could not determine stream of bytestream file, not adding HLT_EXPRESS_OPI_HLT to ESD.")
266
267
268
269
270
271
272
        return False

    def configure(self):

        log = logging.getLogger("HLTTriggerResultGetter.py")
        from RecExConfig.ObjKeyStore import objKeyStore

273
274
275
276
        # Set AODFULL for data unless it was set explicitly already
        if TriggerFlags.AODEDMSet.isDefault() and globalflags.DataSource()=='data':
            TriggerFlags.AODEDMSet = 'AODFULL'
            
277
278
        from AthenaCommon.AlgSequence import AlgSequence
        topSequence = AlgSequence()
279
        log.info("BS unpacking (TF.readBS): %d", TriggerFlags.readBS() )
280
        if TriggerFlags.readBS():
281
282
283
284
            if ConfigFlags.Trigger.EDMVersion == 1 or \
               ConfigFlags.Trigger.EDMVersion == 2:
                bs = ByteStreamUnpackGetterRun1or2()  # noqa: F841
            elif ConfigFlags.Trigger.EDMVersion >=3:
285
                bs = ByteStreamUnpackGetter()  # noqa: F841
286
287
            else:
                raise RuntimeError("Invalid EDMVersion=%s " % ConfigFlags.Trigger.EDMVersion)
288

289
        xAODContainers = {}
290

291
        if ConfigFlags.Trigger.EDMVersion == 1:
292
293
            xaodcnvrt = xAODConversionGetter()
            xAODContainers = xaodcnvrt.xaodlist
294

295
296
297
298
299
300
301
302
303
        if ConfigFlags.Trigger.EDMVersion == 1 or \
           ConfigFlags.Trigger.EDMVersion == 2:
            if rec.doTrigger() or TriggerFlags.doTriggerConfigOnly():
                tdt = TrigDecisionGetterRun1or2()  # noqa: F841
        elif ConfigFlags.Trigger.EDMVersion >= 3:
            if TriggerFlags.readBS():
                tdt = TrigDecisionGetter()  # noqa: F841
        else:
            raise RuntimeError("Invalid EDMVersion=%s " % ConfigFlags.Trigger.EDMVersion)
304

305
        # Temporary hack to add Run-3 navigation to ESD and AOD
306
        if (rec.doESD() or rec.doAOD()) and ConfigFlags.Trigger.EDMVersion == 3:
307
308
309
310
311
312
313
314
315
            # The hack with wildcards is needed for BS->ESD because we don't know the exact keys
            # of HLT navigation containers before unpacking them from the BS event.
            objKeyStore._store['streamESD'].allowWildCard(True)
            objKeyStore._store['streamAOD'].allowWildCard(True)
            objKeyStore.addManyTypesStreamESD(['xAOD::TrigCompositeContainer#HLTNav*',
                                               'xAOD::TrigCompositeAuxContainer#HLTNav*'])
            objKeyStore.addManyTypesStreamAOD(['xAOD::TrigCompositeContainer#HLTNav*',
                                               'xAOD::TrigCompositeAuxContainer#HLTNav*'])

316
317
318
319
320
321
322
323
324
        # TrigJetRec additions
        if rec.doWriteESD():
            objKeyStore.addStreamESD("JetKeyDescriptor","JetKeyMap")
            objKeyStore.addStreamESD("JetMomentMap","TrigJetRecMomentMap")

        if rec.doWriteAOD():
            objKeyStore.addStreamAOD("JetKeyDescriptor","JetKeyMap")
            objKeyStore.addStreamAOD("JetMomentMap","TrigJetRecMomentMap")
                    
325
        if rec.doAOD() or rec.doWriteAOD():
326
327
            # schedule the RoiDescriptorStore conversion
            # log.warning( "HLTTriggerResultGetter - setting up RoiWriter" )
328
329
330
331
            roiWriter = RoiWriter()
            # Add fictional input to ensure data dependency in AthenaMT
            roiWriter.ExtraInputs += [("TrigBSExtractionOutput", "StoreGateSvc+TrigBSExtractionOutput")]
            topSequence += roiWriter
332
            # write out the RoiDescriptorStores
333
            from TrigEDMConfig.TriggerEDMRun2 import TriggerRoiList
334
335
            objKeyStore.addManyTypesStreamAOD( TriggerRoiList )

336
337
338
        #Are we adding operational info objects in ESD?
        added=self._AddOPIToESD()
        if added:
339
            log.debug("Operational Info object HLT_EXPRESS_OPI_HLT with extra information about express stream prescaling added to the data.")
340
341
342
343
344
345
        


        # ESD objects definitions
        _TriggerESDList = {}

346
        from TrigEDMConfig.TriggerEDM import getTriggerEDMList 
347
348
349
350
351
        # we have to store xAOD containers in the root file, NOT AOD,
        # if the xAOD container list is not empty
        if(xAODContainers):
            _TriggerESDList.update( xAODContainers )
        else:
352
            _TriggerESDList.update( getTriggerEDMList(TriggerFlags.ESDEDMSet(),  ConfigFlags.Trigger.EDMVersion) ) 
353
        
354
        log.info("ESD content set according to the ESDEDMSet flag: %s and EDM version %d", TriggerFlags.ESDEDMSet(), ConfigFlags.Trigger.EDMVersion)
355
356
357
358

        # AOD objects choice
        _TriggerAODList = {}
        
359
        #from TrigEDMConfig.TriggerEDM import getAODList    
360
        _TriggerAODList.update( getTriggerEDMList(TriggerFlags.AODEDMSet(),  ConfigFlags.Trigger.EDMVersion) ) 
361

362
        log.info("AOD content set according to the AODEDMSet flag: %s and EDM version %d", TriggerFlags.AODEDMSet(),ConfigFlags.Trigger.EDMVersion)
363

364
365
        log.debug("ESD EDM list: %s", _TriggerESDList)
        log.debug("AOD EDM list: %s", _TriggerAODList)
366
        
367
368
369
370
371
372
373
374
375
376
377
378
        # Highlight what is in AOD list but not in ESD list, as this can cause
        # the "different number of entries in branch" problem, when it is in the
        # AOD list but the empty container per event is not created
        # Just compares keys of dicts, which are the class names, not their string keys in StoreGate
        not_in = [ element for element in  _TriggerAODList if element not in _TriggerESDList ]
        if (len(not_in)>0):
            log.warning("In AOD list but not in ESD list: ")
            log.warning(not_in)
        else:
            log.info("AOD list is subset of ESD list - good.")


379
        def _addSlimmingRun2(stream, edm):
380
            from TrigNavTools.TrigNavToolsConfig import navigationThinningSvc
381

382
383
            edmlist = list(y.split('-')[0] for x in edm.values() for y in x) #flatten names
          
384
385
386
387
            # TimM Sep 2021: In MT the 'reload' slimming option in the R2 navigation thinning service was found to be creating
            # AODs which would crash when trying to return features. We therefore remove this option by using the added 'cleanup_noreload'
            # configuration, see ATR-24141 for details. 
            svc = navigationThinningSvc ({'name':'HLTNav_%s'%stream, 'mode':'cleanup_noreload', 
388
389
390
391
392
393
394
395
                                          'result':'HLTResult_HLT',
                                          'features':edmlist})

            from OutputStreamAthenaPool.CreateOutputStreams import registerTrigNavThinningSvc
            registerTrigNavThinningSvc (stream, svc)

            log.info("Configured slimming of HLT for %s", stream)
            print(svc)  # noqa: ATL901
396
397
398
            del edmlist


399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        if ConfigFlags.Trigger.EDMVersion == 1 or ConfigFlags.Trigger.EDMVersion == 2:

            # Run 1, 2 slimming
            if TriggerFlags.doNavigationSlimming() and rec.readRDO() and rec.doWriteAOD():
                _addSlimmingRun2('StreamAOD', _TriggerESDList ) #Use ESD item list also for AOD!
                log.info("configured navigation slimming for AOD output")
                
            if TriggerFlags.doNavigationSlimming() and rec.readRDO() and rec.doWriteESD():
                _addSlimmingRun2('StreamESD', _TriggerESDList )                
                log.info("configured navigation slimming for ESD output")

        if ConfigFlags.Trigger.EDMVersion >= 3:
            # Change in the future to 'if EDMVersion >= 3 or doEDMVersionConversion:'

            # Run 3 slimming
            if ConfigFlags.Trigger.doNavigationSlimming: 
                from TrigNavSlimmingMT.TrigNavSlimmingMTConfig import getTrigNavSlimmingMTConfig
                from AthenaCommon.Configurable import Configurable
                Configurable.configurableRun3Behavior += 1
                from AthenaConfiguration.ComponentAccumulator import appendCAtoAthena
                appendCAtoAthena( getTrigNavSlimmingMTConfig(ConfigFlags) )
                Configurable.configurableRun3Behavior -= 1
            else:
                log.info("doNavigationSlimming is False, won't schedule run 3 navigation slimming")
423

424
        objKeyStore.addManyTypesStreamESD( _TriggerESDList )
425
426
427
428
429
430
431
432
        objKeyStore.addManyTypesStreamAOD( _TriggerAODList )        
            
        return True