ATLAS Offline Software
Loading...
Searching...
No Matches
JetEfficiencyMonitorAlgorithm.py
Go to the documentation of this file.
2# Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3#
5 '''Function to configure LVL1 JetEfficiency algorithm in the monitoring system.'''
6
7 # get the component factory - used for getting the algorithms
8 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
9 from AthenaConfiguration.ComponentFactory import CompFactory
10 from TrigConfigSvc.TriggerConfigAccess import getL1MenuAccess
11 result = ComponentAccumulator()
12 l1menu = getL1MenuAccess(flags)
13
14
15 from AthenaConfiguration.Enums import Format
16
18 if flags.Input.Format is Format.POOL and not flags.Input.isMC and not any(["AOD" in a for a in flags.Input.ProcessingTags]):
19 from JetRecConfig.JetRecConfig import JetRecCfg
20 from JetRecConfig.StandardSmallRJets import AntiKt4EMPFlow
21 result.merge( JetRecCfg(flags,AntiKt4EMPFlow) )
22
23 from eflowRec.PFCfg import PFGlobalFlowElementLinkingCfg
24 if flags.DQ.Environment == "AOD":
25 result.merge(PFGlobalFlowElementLinkingCfg(flags, useMuonTopoClusters=True))
26 else:
27 result.merge(PFGlobalFlowElementLinkingCfg(flags))
28 from eflowRec.PFCfg import PFGlobalFlowElementLinkingCfg
29 result.merge(PFGlobalFlowElementLinkingCfg(flags))
30 from METReconstruction.METAssociatorCfg import METAssociatorCfg
31 result.merge(METAssociatorCfg(flags, 'AntiKt4EMPFlow'))
32 from METUtilities.METMakerConfig import getMETMakerAlg
33 metCA=ComponentAccumulator()
34 metCA.addEventAlgo(getMETMakerAlg('AntiKt4EMPFlow'))
35 result.merge(metCA)
36
37
38 # make the athena monitoring helper
39 from TrigT1CaloMonitoring.LVL1CaloMonitoringConfig import L1CaloMonitorCfgHelper
40 helper = L1CaloMonitorCfgHelper(flags,CompFactory.JetEfficiencyMonitorAlgorithm,'JetEfficiencyMonAlg')
41 groupName = 'JetEfficiencyMonitor' # the monitoring group name is also used for the package name
42 JetEfficiencyMonAlg = helper.alg
43 JetEfficiencyMonAlg.PackageName = groupName
44
45
46
50
51 # We can choose if we want to use pass before prescale, or not when defining our trigger efficiency
52 # generally only want to use pass before prescale when considering the efficiency of a trigger for
53 # internal evaluation of how triggers are behaving
54 # the prescaling is an important feature of real utility if a
55 passedb4Prescale = True
56 JetEfficiencyMonAlg.PassedBeforePrescale = passedb4Prescale
57
58
62
63 #define the various reference triggers
64 hltRandom_reference_triggers = ['HLT_j0_perf_L1RD0_FILLED', 'HLT_j0_perf_pf_ftf_L1RD0_FILLED']
65 JetEfficiencyMonAlg.HLTRandomReferenceTriggers = hltRandom_reference_triggers
66
67 muon_reference_triggers = ["L1_MU14FCH", "L1_MU18VFCH", "L1_2MU8F", "L1_MU8VF_2MU5VF", "L1_3MU3VF", "L1_MU5VF_3MU3VF", "L1_4MU3V", "L1_2MU5VF_3MU3V"]
68 JetEfficiencyMonAlg.MuonReferenceTriggers = muon_reference_triggers
69
70 JetEfficiencyMonAlg.BootstrapReferenceTrigger='L1_J15'
71
72 trigPath = 'Developer/JetEfficiency/'
73 ExpertTrigPath = 'Expert/Efficiency/'
74 distributionPath = 'Distributions/'
75 noRefPath = 'NoReferenceTrigger/'
76 muonRefPath = 'MuonReferenceTrigger/'
77 randomRefPath = 'RandomHLTReferenceTrigger/'
78 bsRefPath = 'BootstrapReferenceTrigger/'
79 GeV = 1000
80
81 # add monitoring algorithm to group, with group name and main directory
82 gfex_SR_triggers = ['L1_gJ20p0ETA25', 'L1_gJ50p0ETA25', 'L1_gJ100p0ETA25', 'L1_gJ400p0ETA25']
83 gfex_LR_triggers = ['L1_gLJ80p0ETA25', 'L1_gLJ100p0ETA25', 'L1_gLJ140p0ETA25', 'L1_gLJ160p0ETA25']
84
85 jfex_SR_triggers = ['L1_jJ30','L1_jJ40','L1_jJ50', 'L1_jJ60', 'L1_jJ80','L1_jJ90', 'L1_jJ125','L1_jJ140','L1_jJ160', 'L1_jJ180']
86 jfex_LR_triggers = ['L1_SC175-SCjJ10']
87
88 all_SR_triggers = gfex_SR_triggers + jfex_SR_triggers
89 all_LR_triggers = gfex_LR_triggers + jfex_LR_triggers
90
91 # if the trigger isnt included in the menu, then we dont actually want to fill anything into the histograms (it looks weird)
92 JetEfficiencyMonAlg.SmallRadiusJetTriggers_phase1 = [trigger for trigger in all_SR_triggers if trigger in l1menu]
93 JetEfficiencyMonAlg.LargeRadiusJetTriggers_phase1 = [trigger for trigger in all_LR_triggers if trigger in l1menu]
94 # if a trigger isnt in the menu, but its a gfex trigger so we can emulate the efficiemcy using the gFEX TOBs
95 JetEfficiencyMonAlg.SmallRadiusJetTriggers_gFEX = [trigger for trigger in gfex_SR_triggers if trigger not in l1menu]
96 JetEfficiencyMonAlg.LargeRadiusJetTriggers_gFEX = [trigger for trigger in gfex_LR_triggers if trigger not in l1menu]
97
98 # if there are no gFEX triggers in our list that aren't present in the menu, then we don't need to even look at them, so lets just not open them
99 if len(JetEfficiencyMonAlg.SmallRadiusJetTriggers_gFEX ) == 0: JetEfficiencyMonAlg.mygFexSRJetRoIContainer = ""
100 if len(JetEfficiencyMonAlg.LargeRadiusJetTriggers_gFEX ) == 0: JetEfficiencyMonAlg.mygFexLRJetRoIContainer = ""
101
102
103 reference_paths = {"Muon" : muonRefPath, "RandomHLT": randomRefPath, "No": noRefPath, "Bootstrap": bsRefPath}
104 references = ["Muon", "No", "Bootstrap"] #"RandomHLT"
105
106 # if we want to make the eta efficiencies, can add in SReta and LReta
107 sr_props = ["SRpt"] #SReta
108 lr_props = ["LRpt"] #LReta
109 trigger_group_list = {"gfex_SR_triggers" : gfex_SR_triggers,
110 "gfex_LR_triggers" : gfex_LR_triggers,
111 "jfex_SR_triggers" : jfex_SR_triggers,
112 "jfex_LR_triggers" : jfex_LR_triggers }
113 properties_per_trigger_group = {"gfex_SR_triggers" : sr_props, "jfex_SR_triggers" : sr_props,
114 "gfex_LR_triggers" : lr_props, "jfex_LR_triggers" : lr_props }
115 pathadd_per_trigger_group = {"gfex_SR_triggers" : "gFEX/", "jfex_SR_triggers" : "jFEX/",
116 "gfex_LR_triggers" : "gFEX/", "jfex_LR_triggers" : "jFEX/" }
117 trigger_groups = list(trigger_group_list.keys())
118
119 xlabel_for_prop = { "SRpt" :'pT [MeV]', "SReta" : '#eta', "LRpt" :'pT [MeV]', "LReta" : '#eta'}
120 nbins = {"SRpt": 220, "SReta" :32, "LRpt": 220, "LReta" :32}
121 binmin = {"SRpt": -50, "SReta" :-3.3, "LRpt": -50, "LReta" :-3.3}
122 binmax = {"SRpt": 1800*GeV, "SReta" :3.3, "LRpt": 1800*GeV, "LReta" :3.3}
123
124
125 plotDistrubutions = False
126 if plotDistrubutions:
127 helper.defineHistogram('raw_pt',title='pT for all leading offline jets (with no trigger requirments);PT [MeV];Events', fillGroup=groupName, path=trigPath + distributionPath, xbins=nbins["SRpt"], xmin=binmin["SRpt"], xmax=binmax["SRpt"])
128
129 helper.defineHistogram('raw_eta', title='Eta Distribution for all leading offline jets (with no trigger requirments);#eta; Count', fillGroup=groupName, path=trigPath + distributionPath, xbins=nbins["SReta"], xmin=binmin["SReta"], xmax=binmax["SReta"])
130
131
132 for tgroup in trigger_groups: #iterate through the trigger groups (gFEX SR & LR, jFEX SR & LR)
133 for t in trigger_group_list[tgroup]: #iterate through the triggers within subgroups
134 #add algorithm that flags if the efficiency is not reaching 100%
135 # assemble thresholdConfig dict
136 thresholdConfig = {"Plateau":plateau_dict.get(t,[0.99,0.95])}
137 thresholdConfig["Threshold"] = get_or_estimate_thresholds(t)
138 xMaxConfig = min(thresholdConfig["Threshold"][1]*2.5, binmax["SRpt"]) #set the x maximum of the fit to be 6 times the upper limit, to help the fit work better
139 # if t in threshold_dict: thresholdConfig["Threshold"] = threshold_dict[t]
140 helper.defineDQAlgorithm("JetEfficiency_"+t,
141 hanConfig={"libname":"libdqm_algorithms.so","name":"Simple_fermi_Fit_TEff", "xmax":xMaxConfig, "ImproveFit":1}, # this line is always the same
142 thresholdConfig=thresholdConfig
143 )
144 for p in properties_per_trigger_group[tgroup]:
145 for r in references: #iteratate through the refernce trigger options
146
147 # if trigger not included in the menu, then lets modify the hist title to make that clear!
148 if t in l1menu: eff_plot_title = t+';'+xlabel_for_prop[p]+'; Efficiency '
149 elif t not in l1menu and t in (gfex_SR_triggers + gfex_LR_triggers): eff_plot_title = t+' Emulated;'+xlabel_for_prop[p]+'; Efficiency '
150 else: eff_plot_title = t+' NOT in Menu;'+xlabel_for_prop[p]+'; Efficiency '
151
152 #Using the muon reference trigger selection, as our least biased trigger selection inside the web displkay. Others still exist in the HIST file for now
153 if r == "Muon" and p in ["SRpt", "LRpt"]:
154 helper.defineHistogram(f"bool_{r}_{t}, val_{p};{p}_{t}", type='TEfficiency', title=eff_plot_title, fillGroup=groupName, path=ExpertTrigPath + pathadd_per_trigger_group[tgroup]+ reference_paths[r], xbins=nbins[p], xmin=binmin[p], xmax=binmax[p], hanConfig={"algorithm":"JetEfficiency_"+t}, opt='kAlwaysCreate')
155 else:
156 helper.defineHistogram(f"bool_{r}_{t}, val_{p};{p}_{t}", type='TEfficiency', title=eff_plot_title, fillGroup=groupName, path=trigPath + pathadd_per_trigger_group[tgroup]+ reference_paths[r], xbins=nbins[p], xmin=binmin[p], xmax=binmax[p], opt='kAlwaysCreate')
157
158
159
160 acc = helper.result()
161 result.merge(acc)
162 print("flags.DQ.Environment = " + flags.DQ.Environment )
163 return result
164
165def get_closest_threshold(trigger_value, extracted_values, lower_bounds, upper_bounds):
166 """
167 Finds and returns the threshold values of the closest existing trigger.
168 """
169 import numpy as np
170 closest_index = np.abs(extracted_values - trigger_value).argmin()
171 return [lower_bounds[closest_index], upper_bounds[closest_index]]
172
173def estimate_thresold(trigger_name):
174 """
175 Estimates the DQ monitoring threshold range for a given trigger name based on given threshold_dict,
176 treating jet types (gJ, gLJ, jJ, and jLJ) as separate for extrapolation.
177 If interpolation fails, it returns the closest existing trigger values.
178 """
179 import re
180 import numpy as np
181 # Extract the numeric portion and type from the trigger name
182 match = re.search(r'(gJ|gLJ|jJ|jLJ)(\d+)', trigger_name)
183 if not match:
184 return None
185
186 trigger_type, trigger_value = match.groups()
187 trigger_value = int(trigger_value)
188
189 # Gather existing numeric values and their thresholds for the same type
190 extracted_values = []
191 lower_bounds = []
192 upper_bounds = []
193
194 for key, (low, high) in threshold_dict.items():
195 key_match = re.search(r'(gJ|gLJ|jJ|jLJ)(\d+)', key)
196 if key_match and key_match.group(1) == trigger_type:
197 extracted_values.append(int(key_match.group(2)))
198 lower_bounds.append(low)
199 upper_bounds.append(high)
200
201 if not extracted_values:
202 return None # No matching trigger types found
203
204 extracted_values = np.array(extracted_values)
205 lower_bounds = np.array(lower_bounds)
206 upper_bounds = np.array(upper_bounds)
207
208 # Try interpolation/extrapolation
209 lower_pred = np.interp(trigger_value, extracted_values, lower_bounds, left=lower_bounds[0], right=lower_bounds[-1])
210 upper_pred = np.interp(trigger_value, extracted_values, upper_bounds, left=upper_bounds[0], right=upper_bounds[-1])
211
212 return [lower_pred, upper_pred]
213
215 """
216 Returns the threshold from threshold_dict if it exists.
217 Otherwise, predicts the thresholds using estimate_thresold.
218 """
219 if trigger_name in threshold_dict:
220 return threshold_dict[trigger_name] # Return stored values if available
221 else:
222 print("WARNING, trigger " + trigger_name + " doesn't have predifined thresholds for DQ Algorithm. Estimating thresholds with a fit, or using thresholds from closest exisiting trigger.")
223 print("Please add thresholds to the threshold_dict for trigger " + trigger_name)
224 print(estimate_thresold(trigger_name))
225 return estimate_thresold(trigger_name) # Predict or use closest match
226
227
228plateau_dict = {} #in case there are low stats for some of the triggers, we could modify the threshold ranges for some triggers
229threshold_dict = {"L1_gJ20p0ETA25" : [55e3, 75e3], #hits 50% at 50 for a good run, 100% around 75
230 "L1_gJ50p0ETA25" : [110e3, 180e3], #hits 50% at 109 for a good run, 100% around 180
231 "L1_gJ100p0ETA25" : [195e3, 3000e3],#hits 50% at 185 for a good run, 100% around 300
232 "L1_gJ400p0ETA25" : [620e3, 900e3],#hits 50% at 611 for a good run, 100% around 900
233 "L1_gLJ80p0ETA25" : [125e3, 200e3],#hits 50% at 113 for a good run, 100% around 200
234 "L1_gLJ100p0ETA25" : [150e3, 240e3],#hits 50% at 137 for a good run, 100% around 240
235 "L1_gLJ140p0ETA25" : [210e3, 315e3],#hits 50% at 197 for a good run, 100% around 315
236 "L1_gLJ160p0ETA25" : [240e3, 260e3],#hits 50% at 225 for a good run, 100% around 350
237 "L1_jJ30" : [50e3, 85e3],#hits 50% at 46 for a good run, 100% around 85
238 "L1_jJ40" : [55e3, 90e3],#hits 50% at 47 for a good run, 100% around 90
239 "L1_jJ50" : [60e3, 100e3],#hits 50% at 53 for a good run, 100% around 100
240 "L1_jJ60" : [90e3, 130e3],#hits 50% at 85 for a good run, 100% around 130
241 "L1_jJ80" : [90e3, 155e3],#hits 50% at 86 for a good run, 100% around 155
242 "L1_jJ90" : [110e3, 185e3],#hits 50% at 105 for a good run, 100% around 185
243 "L1_jJ125" : [145e3, 230e3],#hits 50% at 140 for a good run, 100% around 230
244 "L1_jJ140" : [160e3, 225e3],#hits 50% at 150 for a good run, 100% around 255
245 "L1_jJ160" : [180e3, 260e3],#hits 50% at 170 for a good run, 100% around 260
246 "L1_jJ180" : [235e3, 320e3],#hits 50% at 223 for a good run, 100% around 320
247 "L1_SC175-SCjJ10": [195e3, 300e3],#hits 50% at 190 for a good run, 100% around 300
248}
249
250
251if __name__=='__main__':
252 # set debug level for whole job
253 from AthenaCommon.Logging import log
254 from AthenaCommon.Constants import INFO #DEBUG
255 log.setLevel(INFO)
256
257 # set input file and config options
258 from AthenaConfiguration.AllConfigFlags import initConfigFlags
259 flags = initConfigFlags()
260
261 import glob
262
263 inputs = glob.glob('/eos/atlas/atlastier0/rucio/data18_13TeV/physics_Main/00354311/data18_13TeV.00354311.physics_Main.recon.ESD.f1129/data18_13TeV.00354311.physics_Main.recon.ESD.f1129._lb0013._SFO-8._0001.1')
264
265
266 flags.Input.Files = inputs
267 flags.Output.HISTFileName = 'ExampleMonitorOutput_LVL1.root'
268
269 flags.lock()
270
271 flags.dump() # print all the configs
272
273 from AthenaConfiguration.MainServicesConfig import MainServicesCfg
274 from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
275 cfg = MainServicesCfg(flags)
276 cfg.merge(PoolReadCfg(flags))
277
278 JetEfficiencyMonitorCfg = JetEfficiencyMonitoringConfig(flags)
279 cfg.merge(JetEfficiencyMonitorCfg)
280
281
282 # message level for algorithm
283 JetEfficiencyMonitorCfg.getEventAlgo('JetEfficiencyMonAlg').OutputLevel = 1 # 1/2 INFO/DEBUG
284 # options - print all
void print(char *figname, TCanvas *c1)
#define min(a, b)
Definition cfImp.cxx:40
get_closest_threshold(trigger_value, extracted_values, lower_bounds, upper_bounds)