ATLAS Offline Software
Loading...
Searching...
No Matches
JetCalibStepsConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3from AnaAlgorithm.DualUseConfig import isAthena
4if not isAthena:
5
8 import AnaAlgorithm.AlgSequence # noqa: F401
9 import JetRecConfig.JetAnalysisCommon # noqa: F401
10
11from AthenaCommon import Logging
12jcslog = Logging.logging.getLogger('JetCalibStepsConfig')
13
14from JetToolHelpers.HelperConfig import VarToolCfg, HistoInputCfg
15from AthenaConfiguration.ComponentFactory import CompFactory
16from AthenaConfiguration.AutoConfigFlags import GetFileMD
17from AthenaConfiguration.Enums import LHCPeriod
18from PathResolver import PathResolver
19import json
20
21def smearingStep(flags, **configDict):
22 """ Configuration of the Smearing step. """
23
24 configDict.setdefault('OutScale', 'JetSmearedMomentum')
25
26 # HistoReaderMC and HistoReaderData can be specified simultaneously using a single "HistoReader" YAML block.
27 # This should contain the common configuration for both and histNameMC/histNameData entries for the histogram names.
28 # This cannot be used simultaneously with a HistoReaderMC or HistoReaderData block
29 if "HistoReader" in configDict:
30 if "HistoReaderMC" in configDict:
31 raise JetCalibConfigError("Both HistoReader and HistoReaderMC blocks included in YAML config")
32 if "HistoReaderData" in configDict:
33 raise JetCalibConfigError("Both HistoReader and HistoReaderData blocks included in YAML config")
34
35 # Build the HistoReaderMC and HistoReaderData blocks from the HistoReader block
36 histoReader = configDict.pop("HistoReader")
37 histNameMC = histoReader.pop("histNameMC")
38 histNameData = histoReader.pop("histNameData")
39 configDict["HistoReaderMC"] = dict(histoReader)
40 configDict["HistoReaderData"] = dict(histoReader)
41 configDict["HistoReaderMC"]["histName"] = histNameMC
42 configDict["HistoReaderData"]["histName"] = histNameData
43
44 configDict["HistoReaderMC"]["inputFile"] = PathResolver.FindCalibFile(configDict["HistoReaderMC"]["inputFile"])
45 configDict["HistoReaderData"]["inputFile"] = PathResolver.FindCalibFile(configDict["HistoReaderData"]["inputFile"])
46
47 histToolMC = HistoInputCfg(flags, "HistToolMC", **configDict["HistoReaderMC"])
48 histToolData = HistoInputCfg(flags, "HistToolData", **configDict["HistoReaderData"])
49 configDict["HistoReaderMC"] = histToolMC
50 configDict["HistoReaderData"] = histToolData
51
52 smearStep = CompFactory.SmearingCalibStep("SmearingCalibStep", **configDict)
53
54 return [smearStep]
55
56def puresidualStep(flags, **configDict):
57 configDict.setdefault('OutScale', 'JetPileupScaleMomentum')
58 configDict.setdefault('IsData', not flags.Input.isMC)
59 PU_step = CompFactory.Pileup1DResidualCalibStep("PUResid", **configDict)
60 return [PU_step]
61
62def puCorrectionStep(flags, **configDict):
63 # rho * area and histogram-based 1D residual correction
64 configDict.setdefault('OutScale', 'JetPileupScaleMomentum')
65 configDict.setdefault('IsData', not flags.Input.isMC)
66
67 histoParamsMu = configDict.pop('histoParamsMu')
68 inputFile = PathResolver.FindCalibFile(configDict.pop('calibFile'))
69 histoParamsMu['inputFile'] = inputFile
70
71 histoParamsNPV = configDict.pop('histoParamsNPV')
72 histoParamsNPV['inputFile'] = inputFile
73
74 histToolMu = HistoInputCfg(flags, "HistToolMu", **histoParamsMu)
75 histToolNPV = HistoInputCfg(flags, "HistToolNPV", **histoParamsNPV)
76 configDict["histTool_mu"] = histToolMu
77 configDict["histTool_NPV"] = histToolNPV
78
79 PU_step = CompFactory.PileupCalibStep("PileUpCorrection", **configDict)
80 return [PU_step]
81
82def gscStep(flags, **configDict):
83
84 configDict.setdefault('OutScale', 'JetGSCScaleMomentum')
85
86 defaultFileGSC = PathResolver.FindCalibFile(configDict.pop('fileGSC'))
87
88 # These HistoInput2D defaults can't be set in the C++, so are set here:
89 defaultHistTools = dict(
90 histTool_EM3 = [dict(varX = "pt", varY = "EM3", histName=f"AntiKt4EMPFlow_EM3_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(35)],
91 histTool_CharFrac = [dict(varX = "pt", varY = "ChargedFraction", histName = f"AntiKt4EMPFlow_chargedFraction_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(25)],
92 histTool_Tile0 = [dict(varX = "pt", varY = "Tile0", histName=f"AntiKt4EMPFlow_Tile0_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(18)],
93 histTool_nTrk=[dict(varX = "pt", varY = dict(Name="nTrk", Type="int",), histName=f"AntiKt4EMPFlow_nTrk_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(25)],
94 histTool_trackWIDTH=[dict(varX = "pt", varY = "trackWIDTH", histName=f"AntiKt4EMPFlow_trackWIDTH_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(25)],
95 histTool_PunchThrough=[dict(varX = "e", varY = dict(Name="Nsegments", Type="int",), histName=f"AntiKt4EMPFlow_PunchThrough_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(2)],
96 )
97
98 gsc_steps = []
99 if configDict.get('applyChargedFraction', True):
100 gsc_steps.append('histTool_CharFrac')
101 if configDict.get('applyEM3', True):
102 gsc_steps.append('histTool_EM3')
103 if configDict.get('applyTile0', True):
104 gsc_steps.append('histTool_Tile0')
105 if configDict.get('applyNtrk', True):
106 gsc_steps.append('histTool_nTrk')
107 if configDict.get('applyTrackWidth', True):
108 gsc_steps.append('histTool_trackWIDTH')
109 if configDict.get('applyPunchThrough',False):
110 gsc_steps.append('histTool_PunchThrough')
111
112 # Build the hist tools
113 for key in gsc_steps:
114 # Use defaultHistTools by default
115 if key not in configDict:
116 toolArray = defaultHistTools[key]
117
118 # In this case the full list of sub-tools has been specified in the YAML
119 # and the defaultDict will be fully overwritten (ie. all parameters must be specified)
120 elif isinstance(configDict[key],list):
121 toolArray = configDict[key]
122 for subDict in toolArray:
123 subDict.setdefault('inputFile',defaultFileGSC)
124
125 # Functionality to build the arrays of histogram reader from a shorter block in the YAML file
126 # Fall back on defaultHistTools for defaults
127 else:
128 baseDict = dict(configDict[key])
129 N_hist = baseDict.pop('N_hist')
130 histNameBase = baseDict.pop('histNameBase')
131 inputFile = baseDict.pop('inputFile', defaultFileGSC)
132 varX = baseDict.pop('varX',defaultHistTools[key][0]['varX'])
133 varY = baseDict.pop('varY', defaultHistTools[key][0]['varY'])
134 toolArray = [dict(varX = varX, varY = varY, histName=f'{histNameBase}_{j}', inputFile = inputFile) for j in range(N_hist)]
135
136 # Convert array of properties to HistoInput tools
137 configDict[key] = [HistoInputCfg(flags, Tname=f"{key.split('_')[1]}_{j}", **toolConfig) for j, toolConfig in enumerate(toolArray)]
138
139 GSCstep = CompFactory.GSCCalibStep("gsccalibstep", **configDict)
140
141 return [GSCstep]
142
143def etajesStep(flags, **configDic):
144 configDic.setdefault('OutScale', 'JetEtaJESScaleMomentum')
145 pVars = configDic.pop("ParametrizedVars")
146
147 jesstep = CompFactory.EtaJESCalibStep("EtaJESCalib",
148 VarToolE= VarToolCfg(flags, var=pVars['varE']),
149 VarToolEta= VarToolCfg(flags, var=pVars["varEta"]),
150 **configDic
151 )
152 return [jesstep]
153
154def jmsStep(flags, **configDic):
155
156 configDic.setdefault('OutScale', 'JetJMSScaleMomentum')
157 histoParams = configDic.pop('histoParams')
158 histoParams['inputFile'] = PathResolver.FindCalibFile(configDic.pop('HistoFile'))
159
160 configDic["histoReaderJMS"] = HistoInputCfg(flags, "HistToolJMS", **histoParams)
161 configDic['varToolX'] = VarToolCfg(flags, var=histoParams['varX'], Tname="VarToolX_JMS")
162 configDic['varToolZ'] = VarToolCfg(flags, var=histoParams['varZ'], Tname="VarToolZ_JMS")
163
164 jmsstep = CompFactory.JMSCalibStep("JMSCalib",
165 **configDic
166 )
167 return [jmsstep]
168
169def insituStep(flags, **configDic):
170 configDic.setdefault('OutScale', 'JetInsituScaleMomentum')
171 histEtaInterCalib = configDic.pop('histEtaInterCalib')
172 histAbsCalib = configDic.pop('histAbsCalib')
173
174 histAbsJMSCalib = configDic.pop('JMS',None)
175
176 histoReaderEta_vec, histoReaderAbs_vec = [], []
177
178 for infile in configDic.pop('fileInsitu'):
179 histoReaderEta_vec.append(dict(inputFile = PathResolver.FindCalibFile(infile), **histEtaInterCalib))
180 histoReaderAbs_vec.append(dict(inputFile = PathResolver.FindCalibFile(infile), **histAbsCalib))
181
182 configDic['HistoReaderEtaInter'] = [HistoInputCfg(flags, "HistToolEtaInter"+str(j), **etaDic) for j, etaDic in enumerate(histoReaderEta_vec)]
183 configDic['HistoReaderAbs'] = [HistoInputCfg(flags, "HistToolAbs"+str(j), **absDic) for j, absDic in enumerate(histoReaderAbs_vec)]
184
185 configDic['isMC'] = flags.Input.isMC
186
187 insituSteps = [CompFactory.InSituCalibStep("insitucalibstep", **configDic)]
188
189 # JMS
190 if histAbsJMSCalib:
191 histAbsJMSCalib['inputFile'] = PathResolver.FindCalibFile(histAbsJMSCalib['inputFile'])
192 insituSteps.append(
193 CompFactory.InSituJMSCalibStep("insitujmscalibstep",
194 CalibrateMC = configDic.get("CalibrateMC",False),
195 isMC = flags.Input.isMC,
196 # modifying insitu scale rather than defining a new scale
197 InScale = "JetInsituScaleMomentum",
198 OutScale = "JetInsituScaleMomentum",
199 HistoReaderAbsJMS = HistoInputCfg(flags, "HistoToolAbsJMS", **histAbsJMSCalib),
200 ))
201
202 return insituSteps
203
204def af3Step(flags, **configDic):
205 configDic.setdefault('OutScale','JetFastSimScaleMomentum')
206 # Get the settings for the histograms:
207 histoParams = configDic.pop('histoParams')
208 histoParams['inputFile'] = PathResolver.FindCalibFile(configDic.pop('CalibConstantFile'))
209 configDic["histoTool"] = HistoInputCfg(flags, "histoTool", **histoParams)
210
211 return [CompFactory.Generic4VecCorrectionStep("AF3", **configDic)]
212
213def ptResidualStep(flags, **configDic):
214 configDic.setdefault('OutScale','JetPtResidualScaleMomentum')
215 # Get the settings for the histograms:
216 histoParams = configDic.pop('histoParams')
217
218 # Define varTool to switch to bin centers
219 if configDic['useBinCenter']:
220 varYHisto = histoParams.pop('varYHisto')
221 configDic['varTool'] = VarToolCfg(flags, var=varYHisto, Tname="VarTool_for_binCenter")
222
223 histoParams['inputFile'] = PathResolver.FindCalibFile(configDic.pop('CalibConstantFile'))
224 # 2D histogram with correction factors
225 configDic["histoTool"] = HistoInputCfg(flags, "histoTool", **histoParams)
226
227 return [CompFactory.Generic4VecCorrectionStep("PtResidual", **configDic)]
228
229def mc2mcStep(flags, **configDic):
230 configDic.setdefault('OutScale','JetMC2MCScaleMomentum')
231 # Generator and version are the first item
232 for key, value in flags.Input.GeneratorsInfo.items():
233 generator = key
234 generator_version = value
235 break
236
237 # Get the shower model:
238 showerModel = ''
239 # Check first if the DSID is on the exceptions list
240 mcDSID = flags.Input.MCChannelNumber
241 with open(PathResolver.FindCalibFile("JetCalibTools/MC2MC_exceptions_DSID.json")) as read_file:
242 data = json.load(read_file)
243 for key, value in data.items():
244 if key == mcDSID:
245 showerModel = value
246
247 if showerModel == '':
248 genType, psType, hadType = generatorDic[generator]
249 version = generator_version.replace('.','')[:3]
250 if (generator == 'Pythia8' or generator == 'Pythia8B') and not version.startswith('8'):
251 version = '8'+version
252 showerModel = genType+"-"+version+"-"+psType+"-"+hadType
253
254 with open(PathResolver.FindCalibFile("JetCalibTools/MC2MC_showerRemap.json")) as read_file:
255 data = json.load(read_file)
256 foundMatch = False
257 for key, value in data.items():
258 if key == showerModel:
259 showerModel = value
260 foundMatch = True
261 break
262 if not foundMatch:
263 for key, value in data.items():
264 if key == genType+"-"+version:
265 showerModel = value+"-"+psType+"-"+hadType
266 break
267
268 jcslog.info(f'Using shower model {showerModel} for the MC-to-MC correction')
269
270 # Get the settings for the histograms:
271 baseHistoParams = configDic.pop('histoParams')
272 baseHistoParams['inputFile'] = PathResolver.FindCalibFile(configDic.pop('CalibConstantFileName')+'_'+showerModel+'.root')
273
274 histNameBase = baseHistoParams.pop('histNameBase')
275 for flav in configDic.pop('flavours'):
276 if flav == 'c':
277 configDic['doCjetCorrection'] = True
278 elif flav == 'b':
279 configDic['doBjetCorrection'] = True
280 histoParams = dict(varX = baseHistoParams['varX'],varY = baseHistoParams['varY'],
281 histName=f'{histNameBase}_{flav}',
282 inputFile=baseHistoParams['inputFile'])
283 configDic['mc2mcHist_'+flav] = HistoInputCfg(flags,Tname='HistoTool_MC2MC_'+flav,**histoParams)
284
285 configDic['isMC2MCCorr'] = True
286
287 return [CompFactory.Generic4VecCorrectionStep("MC2MC", **configDic)]
288
289def jetDNNCalibStep(flags, **configDic):
290 configDic.setdefault('OutScale', 'JetDNNScaleMomentum')
291
292 onnxModelPath = PathResolver.FindCalibFile(configDic.pop('ONNXInput'))
293
294 #Retrieving variables from YAML
295 inputVarArray = configDic.pop('InputVars')
296 eScales = configDic.pop('EScales')
297
298 list_of_inputVarTools = []
299
300 for i, v in enumerate(inputVarArray):
301 if v in ["NPV", "mu"]:
302 list_of_inputVarTools.append(VarToolCfg(flags, v, f'{v}', isJetVar=False))
303 elif v in ["log_e", "log_m"]:
304 # InputVariable.cpp's log_e/log_m already compute log(e*scale)/log(m*scale) with the
305 # right scale-before-log semantics, so just pass eScale through as this VarTool's Scale.
306 list_of_inputVarTools.append(VarToolCfg(flags, v, f'{v}', isJetVar=True, Scale=eScales[i]))
307 else:
308 list_of_inputVarTools.append(VarToolCfg(flags, v, f'{v}', isJetVar=True))
309
310 #Add to configDic
311 configDic["InputVarTool"] = list_of_inputVarTools
312 configDic["EScales"] = eScales
313 configDic["NormOffsets"] = configDic.pop('NormOffsets')
314 configDic["NormScales"] = configDic.pop('NormScales')
315 configDic["onnxInputShape"] = configDic.pop('onnxInputShape')
316 configDic["onnxOutputShape"] = configDic.pop('onnxOutputShape')
317
318 # Set-up the ONNX inference tool, pointing its session at the calibration model from the YAML.
319 # OnnxRuntimeInferenceToolCfg is ComponentAccumulator-based (picks CPU/CUDA via
320 # flags.AthOnnx.ExecutionProvider), but this file builds plain Configurables throughout with no
321 # accumulator to merge into, so we use a throwaway local one and keep only the tool. Its
322 # OnnxRuntimeSvc registration is discarded here; the service still resolves via Gaudi's
323 # auto-create-by-name behaviour, same as before this change.
324 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
325 from AthOnnxComps.OnnxRuntimeInferenceConfig import OnnxRuntimeInferenceToolCfg
326 onnxAcc = ComponentAccumulator()
327 configDic["ORTInferenceTool"] = onnxAcc.popToolsAndMerge(
328 OnnxRuntimeInferenceToolCfg(flags, model_fname=onnxModelPath, name="ORTInferenceTool_DNN")
329 )
330
331 # Set-up the calibration step
332 DNNCalibStep = CompFactory.JetDNNCalibStep("JetDNNCalib", **configDic)
333 return [DNNCalibStep]
334
335
336
337
338calibStepDic = dict(
339 Residual = puresidualStep,
340 Pileup = puCorrectionStep,
341 EtaJES = etajesStep,
342 JMS = jmsStep,
343 GSC = gscStep,
344 Insitu = insituStep,
345 Smear = smearingStep,
346 AF3 = af3Step,
347 PtResidual = ptResidualStep,
348 MC2MC = mc2mcStep,
349 DNNCalib = jetDNNCalibStep
350
351)
352
353
354generatorDic = {
355 "Herwigpp": ["Herwigpp", "angular", "cluster"],
356 "Herwig7": ["Herwig", "angular", "cluster"],
357 "Sherpa": ["Sherpa", "dipole", "cluster"],
358 "Pythia8B": ["PythiaB", "dipole", "cluster"],
359 "Pythia8": ["Pythia", "dipole", "cluster"]
360}
361
363 ''' Returns sample type (AF3/FullSim/Data) and run (Run2/Run3/Run4) '''
364 # Identify type of sample
365 if flags.Input.isMC:
366 metaData = GetFileMD(flags.Input.Files[0])
367 simFlavour = metaData.get('Simulator','') # ATLFAST3 or FullG4
368 if 'ATLFAST3' in simFlavour:
369 sampleType = 'AF3'
370 else:
371 sampleType = 'FullSim'
372 else:
373 sampleType = 'Data'
374
375 if flags.GeoModel.Run == LHCPeriod.Run2:
376 run = 'Run2'
377 elif flags.GeoModel.Run == LHCPeriod.Run3:
378 run = 'Run3'
379 elif flags.GeoModel.Run >= LHCPeriod.Run4:
380 run = 'Run4'
381 else:
382 jcslog.warning('LHCPeriod not recognised')
383 run = None
384
385 return sampleType, run
386
387def sequenceForSample(seqBlock, sampleKeys):
388 """
389 Returns (sequence, matched sample key) for a Sequence sub-block, which is either a plain
390 list of steps (matched key None) or a dict keyed by sample type. sampleKeys is the list of
391 accepted sample keys, in order of preference. Returns (None, None) if nothing matches.
392 """
393 if isinstance(seqBlock, list):
394 return seqBlock, None
395 if isinstance(seqBlock, dict):
396 for key in sampleKeys:
397 if key in seqBlock:
398 return seqBlock[key], key
399 return None, None
400
401def calibConfigToToolList(flags, calibSeqKey = None, **configDict):
402 """
403 Returns a list of instantiated tools for each of the calibration steps.
404 Tools are instantiated by calling functions declared in the calibStepDic dictionary.
405 The order of the steps is determined by the Sequence block of the config.
406
407 Parameters:
408 -----------
409 calibSeqKey: str
410 Specifies which sub-block of Sequence to take the calibration sequence from (e.g. Default/T0/Run3 etc.).
411 If not set, Default is used unless a "RunX" sub-block exists matching the metadata of the sample.
412 Default is also used if the requested sub-block has no entry for this sample type.
413 configDict: dict
414 A dictionary of config options (usually extracted from a YAML config file).
415 """
416
417 sampleKey, runKey = getSampleMetadata(flags)
418
419 # 'MC' can be used in the YAML as a shorthand covering both FullSim and AF3
420 sampleKeys = [sampleKey] if sampleKey=='Data' else [sampleKey, 'MC']
421
422 # Ordering of calib steps based on Sequence block and type of sample
423 if 'Sequence' not in configDict:
424 raise JetCalibConfigError("Sequence block not found in YAML file.")
425 seqDict = configDict.pop('Sequence')
426
427 # Use the run-specific sequence if one is supplied for this sample, otherwise Default
428 if calibSeqKey is None:
429 calibSeqKey = runKey if sequenceForSample(seqDict.get(runKey), sampleKeys)[0] else 'Default'
430
431 if calibSeqKey not in seqDict:
432 raise JetCalibConfigError(f"{calibSeqKey} key not found in YAML Sequence block")
433
434 # Extract the step sequence for this sample type
435 sequence, matchedKey = sequenceForSample(seqDict[calibSeqKey], sampleKeys)
436
437 # Fall back on Default if the requested sub-block has no entry for this sample type
438 if sequence is None and calibSeqKey!='Default':
439 jcslog.warning(f'No {sampleKey} sequence in Sequence[{calibSeqKey}] - falling back on Default')
440 calibSeqKey = 'Default'
441 sequence, matchedKey = sequenceForSample(seqDict.get('Default'), sampleKeys)
442
443 # Raise an error if the sequence for this sample could not be identified
444 if sequence is None:
445 raise JetCalibConfigError(f'No sequence for sample type "{sampleKey}" found in Sequence[{calibSeqKey}] YAML sub-block')
446
447 jcslog.info(f'Using {calibSeqKey} step sequence' + (f' for {matchedKey}' if matchedKey else ''))
448
449 toolList = []
450 jcslog.debug('Configuring jet calib steps:')
451 for step in sequence:
452
453 if step not in configDict:
454 raise JetCalibConfigError(f'Sequence includes step {step} but no YAML block is provided.')
455
456 calibConfig = configDict[step]
457 calibConfig.pop('prereqs',{}) # removes the 'prereqs' entry not refined in steps
458
459 # expert option to skip a step
460 if calibConfig.pop('noRun',False):
461 jcslog.warning(f'Expert option: Skipping calib step {step}')
462 continue
463
464 # Warning: Insitu for MC
465 if step=="Insitu" and flags.Input.isMC and not calibConfig.get("CalibrateMC",False):
466 jcslog.warning('Insitu step included for MC but CalibrateMC is False - no calibration will be run')
467
468 if step=="MC2MC":
469 # Print warning if running MC2MC calibration for data
470 if not flags.Input.isMC:
471 jcslog.warning('Running MC2MC calibration for data')
472
473 # Skip MC to MC calibration for Pythia8
474 generator = next(iter(flags.Input.GeneratorsInfo), '')
475 if 'Pythia' in generator:
476 jcslog.debug('Skipping MC2MC calibration for Pythia8')
477 continue
478
479 # Warning if running AF3 calibration for data or FullSim
480 if step=="AF3":
481 if not flags.Input.isMC:
482 jcslog.warning('Running FastSimulation calibration for data')
483
484 if sampleKey=='FullSim':
485 jcslog.warning('Running FastSimulation calibration for full sim')
486
487 calibFunc = calibStepDic.get(step,None)
488
489 if calibFunc is None:
490 raise NotImplementedError(f'Calibration step {step} is not found in calibStepDic')
491
492 # Config can contain run-specific settings in 'RunX:' sub-blocks. All are removed from
493 # the configDict, but only the block matching this sample's Run is applied.
494 runOverrides = {key: calibConfig.pop(key) for key in ['Run2', 'Run3', 'Run4'] if key in calibConfig}
495 for key, value in runOverrides.get(runKey,{}).items():
496 jcslog.debug(f'{step}: Applying {runKey} override for {key}')
497 if key in calibConfig:
498 jcslog.warning(f'{key} will be overwritten by {runKey} settings')
499 calibConfig[key] = value
500
501 # Start from ConstitScale. For subsequent steps set InScale to OutScale of previous step
502 if len(toolList)==0:
503 inScale = 'JetConstitScaleMomentum'
504 else:
505 inScale = toolList[-1].OutScale
506
507 configInScale = calibConfig.setdefault('InScale', inScale)
508
509 if configInScale!=inScale:
510 jcslog.warning(f'InScale set to {configInScale} in YAML config, but expected {inScale} from Sequence ordering -- is this intentional?')
511
512
513 # each func returns a list (to allow one YAML block to configure multiple steps run in order)
514 newToolList = calibFunc(flags, **calibConfig)
515 jcslog.debug(f'{step}: InScale = {newToolList[0].InScale}, OutScale = {newToolList[-1].OutScale}')
516
517 toolList += newToolList
518
519 return toolList
520
521def calibToolFromConfigFile(flags, configFile, name = "jetcalib", calibSeqKey = None):
522 """
523 Returns a list of instantiated tools for each of the calibration steps.
524 The order of the steps is determined by the Sequence block of the config.
525
526 Parameters:
527 -----------
528 configFile: str
529 Path to YAML configuration file
530 name: str
531 Internal name of the configured jet calib tool
532 calibSeqKey: str
533 Specifies which sub-block of Sequence to take the calibration sequence from (e.g. Default/T0/Run3 etc.).
534 If not set, Default is used unless a "RunX" sub-block exists matching the metadata of the sample.
535 Default is also used if the requested sub-block has no entry for this sample type.
536 """
537 infoMsg = f'Configuring JetCalibTools with {configFile}'
538 jcslog.info(infoMsg)
539
540 configDic = load_yaml_cfg(configFile)
541
542 globalSettings = configDic.pop('Global',{})
543
544 calibTool = CompFactory.JetCalibTool(name, CalibSteps=calibConfigToToolList(flags, calibSeqKey, **configDic), **globalSettings)
545 return calibTool
546
547def combine(baseDict, extendDict):
548 ''' Return baseDict with keys recursively overwritten by extendDict '''
549 merged = dict(baseDict)
550 for key, value in extendDict.items():
551 if isinstance(merged.get(key), dict) and isinstance(value, dict):
552 merged[key] = combine(merged[key], value)
553 else:
554 merged[key] = value
555 return merged
556
557
558def load_yaml_cfg(configFile):
559 from yaml import safe_load
560
561 path_configFile = PathResolver.FindCalibFile(configFile)
562 configDic = safe_load(open(path_configFile))
563
564 # If configDic includes the 'BaseConfig' keyword, it will be combined with the specified base config file
565 baseConfigFile = configDic.pop('BaseConfig',None)
566 if not baseConfigFile:
567 return configDic
568 else:
569 jcslog.info(f'Extending base config file {baseConfigFile} with supplied overrides')
570 # Combine config files recursively
571 baseConfigDic = load_yaml_cfg(baseConfigFile)
572 combinedConfig = combine(baseConfigDic, configDic)
573 return combinedConfig
574
575class JetCalibConfigError(Exception):
576 """ Exception raised for invalid jet calibration config """
577
578 def __init__(self, message):
579 super().__init__(message)
580 self.message = message
581
582 def __str__(self):
583 return self.message
static std::string FindCalibFile(const std::string &logical_file_name)
smearingStep(flags, **configDict)
jmsStep(flags, **configDic)
combine(baseDict, extendDict)
puresidualStep(flags, **configDict)
insituStep(flags, **configDic)
ptResidualStep(flags, **configDic)
af3Step(flags, **configDic)
calibToolFromConfigFile(flags, configFile, name="jetcalib", calibSeqKey=None)
mc2mcStep(flags, **configDic)
sequenceForSample(seqBlock, sampleKeys)
puCorrectionStep(flags, **configDict)
calibConfigToToolList(flags, calibSeqKey=None, **configDict)
etajesStep(flags, **configDic)
jetDNNCalibStep(flags, **configDic)
gscStep(flags, **configDict)