ATLAS Offline Software
JetCalibStepsConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 from AnaAlgorithm.DualUseConfig import isAthena
3 if not isAthena:
4  # importing this package will prepare replacement modules missing in AnalysisBase
5  import JetRecConfig.JetAnalysisCommon # noqa: F401
6 from AthenaCommon import Logging
7 jcslog = Logging.logging.getLogger('JetCalibStepsConfig')
8 
9 from JetToolHelpers.HelperConfig import VarToolCfg, HistoInputCfg
10 from AthenaConfiguration.ComponentFactory import CompFactory
11 from PathResolver import PathResolver
12 
13 def smearingStep(flags, **configDict):
14  """ Configuration of the Smearing step. """
15 
16  # HistoReaderMC and HistoReaderData can be specified simultaneously using a single "HistoReader" YAML block.
17  # This should contain the common configuration for both and histNameMC/histNameData entries for the histogram names.
18  # This cannot be used simultaneously with a HistoReaderMC or HistoReaderData block
19  if "HistoReader" in configDict:
20  if "HistoReaderMC" in configDict:
21  raise JetCalibConfigError("Both HistoReader and HistoReaderMC blocks included in YAML config")
22  if "HistoReaderData" in configDict:
23  raise JetCalibConfigError("Both HistoReader and HistoReaderData blocks included in YAML config")
24 
25  # Build the HistoReaderMC and HistoReaderData blocks from the HistoReader block
26  histoReader = configDict.pop("HistoReader")
27  histNameMC = histoReader.pop("histNameMC")
28  histNameData = histoReader.pop("histNameData")
29  configDict["HistoReaderMC"] = dict(histoReader)
30  configDict["HistoReaderData"] = dict(histoReader)
31  configDict["HistoReaderMC"]["histName"] = histNameMC
32  configDict["HistoReaderData"]["histName"] = histNameData
33 
34  configDict["HistoReaderMC"]["inputFile"] = PathResolver.FindCalibFile(configDict["HistoReaderMC"]["inputFile"])
35  configDict["HistoReaderData"]["inputFile"] = PathResolver.FindCalibFile(configDict["HistoReaderData"]["inputFile"])
36 
37  histToolMC = HistoInputCfg(flags, "HistToolMC", **configDict["HistoReaderMC"])
38  histToolData = HistoInputCfg(flags, "HistToolData", **configDict["HistoReaderData"])
39  configDict["HistoReaderMC"] = histToolMC
40  configDict["HistoReaderData"] = histToolData
41 
42  smearStep = CompFactory.SmearingCalibStep("SmearingCalibStep", **configDict)
43 
44  return smearStep
45 
46 def puresidualStep(flags, **configDict):
47 
48  configDict.setdefault('IsData', not flags.Input.isMC)
49 
50  return CompFactory.Pileup1DResidualCalibStep("PUResid", **configDict)
51 
52 
53 def gscStep(flags, **configDict):
54 
55  defaultFileGSC = PathResolver.FindCalibFile(configDict.pop('fileGSC'))
56 
57  # These HistoInput2D defaults can't be set in the C++, so are set here:
58  defaultHistTools = dict(
59  histTool_EM3 = [dict(varX = "pt", varY = "EM3", histName=f"AntiKt4EMPFlow_EM3_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(35)],
60  histTool_CharFrac = [dict(varX = "pt", varY = "ChargedFraction", histName = f"AntiKt4EMPFlow_chargedFraction_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(25)],
61  histTool_Tile0 = [dict(varX = "pt", varY = "Tile0", histName=f"AntiKt4EMPFlow_Tile0_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(18)],
62  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)],
63  histTool_trackWIDTH=[dict(varX = "pt", varY = "trackWIDTH", histName=f"AntiKt4EMPFlow_trackWIDTH_interpolation_resp_eta_{j}", inputFile=defaultFileGSC) for j in range(25)],
64  )
65 
66  # Build the hist tools
67  for key in ['histTool_EM3', 'histTool_CharFrac', 'histTool_Tile0', 'histTool_nTrk', 'histTool_trackWIDTH']:
68 
69  # Use defaultHistTools by default
70  if key not in configDict:
71  toolArray = defaultHistTools[key]
72 
73  # In this case the full list of sub-tools has been specified in the YAML
74  # and the defaultDict will be fully overwritten (ie. all parameters must be specified)
75  elif isinstance(configDict[key],list):
76  toolArray = configDict[key]
77  for subDict in toolArray:
78  subDict.setdefault('inputFile',defaultFileGSC)
79 
80  # Functionality to build the arrays of histogram reader from a shorter block in the YAML file
81  # Fall back on defaultHistTools for defaults
82  else:
83  baseDict = dict(configDict[key])
84  N_hist = baseDict.pop('N_hist')
85  histNameBase = baseDict.pop('histNameBase')
86  inputFile = baseDict.pop('inputFile', defaultFileGSC)
87  varX = baseDict.pop('varX',defaultHistTools[key][0]['varX'])
88  varY = baseDict.pop('varY', defaultHistTools[key][0]['varY'])
89  toolArray = [dict(varX = varX, varY = varY, histName=f'{histNameBase}_{j}', inputFile = inputFile) for j in range(N_hist)]
90 
91  # Convert array of properties to HistoInput tools
92  configDict[key] = [HistoInputCfg(flags, Tname=f"{key.split('_')[1]}_{j}", **toolConfig) for j, toolConfig in enumerate(toolArray)]
93 
94  GSCstep = CompFactory.GSCCalibStep("gsccalibstep", **configDict)
95 
96  return GSCstep
97 
98 def etajesStep(flags, **configDic):
99 
100  pVars = configDic.pop("ParametrizedVars")
101 
102  jesstep = CompFactory.EtaMassJESCalibStep("EtaMassJESCalib",
103  VarToolE= VarToolCfg(flags, var=pVars['varE']),
104  VarToolEta= VarToolCfg(flags, var=pVars["varEta"]),
105  **configDic
106  )
107  return jesstep
108 
109 def insituStep(flags, **configDic):
110 
111  histEtaInterCalib = configDic.pop('histEtaInterCalib')
112  histAbsCalib = configDic.pop('histAbsCalib')
113 
114  histoReaderEta_vec, histoReaderAbs_vec = [], []
115 
116  for infile in configDic.pop('fileInsitu'):
117  histoReaderEta_vec.append(dict(inputFile = PathResolver.FindCalibFile(infile), **histEtaInterCalib))
118  histoReaderAbs_vec.append(dict(inputFile = PathResolver.FindCalibFile(infile), **histAbsCalib))
119  configDic['HistoReaderEtaInter'] = [HistoInputCfg(flags, "HistToolEtaInter"+str(j), **etaDic) for j, etaDic in enumerate(histoReaderEta_vec)]
120  configDic['HistoReaderAbs'] = [HistoInputCfg(flags, "HistToolAbs"+str(j), **absDic) for j, absDic in enumerate(histoReaderAbs_vec)]
121 
122  configDic['vartool1'] = VarToolCfg(flags, var=histEtaInterCalib['varX'], Tname="VarTool")
123  configDic['vartool2'] = VarToolCfg(flags, var=histEtaInterCalib['varY'], Tname="VarTool")
124  configDic['isMC'] = flags.Input.isMC
125 
126  insituStep = CompFactory.InSituCalibStep("insitucalibstep", **configDic)
127 
128  return insituStep
129 
130 
131 
132 calibStepDic = dict(
133  JetArea = None,
134  Residual = puresidualStep,
135  EtaJES = etajesStep,
136  GSC = gscStep,
137  Insitu = insituStep,
138  Smear = smearingStep,
139 )
140 
141 def calibConfigToToolList(flags, **configDict):
142  """
143  Returns a list of instantiated tools for each of the calibration steps.
144  The order of the steps is determined by the InScale and OutScale properties given in the config.
145  Tools are instantiated by calling functions declared in the calibStepDic dictionary.
146  """
147 
148  toolDic = {}
149  foundCS = False # check at least one of the steps starts from constituent scale
150  for step in configDict:
151 
152  configDict.get(step).pop('prereqs',{}) # removes the 'prereqs' entry not refined in steps
153  # expert option to skip a step
154  if configDict.get(step).pop('noRun',False):
155  jcslog.warning(f'Expert option: Skipping calib step {step}')
156  continue
157 
158  # Skip Insitu for MC
159  if step=="Insitu" and flags.Input.isMC and not configDict.get("Insitu").get("CalibrateMC",False):
160  jcslog.info('Skipping Insitu for MC')
161  continue
162 
163  calibFunc = calibStepDic.get(step,None)
164  if calibFunc is None:
165  raise NotImplementedError(f'Calibration step {step} is not found in calibStepDic')
166 
167  calibConfig = configDict.get(step)
168 
169  tool = calibFunc(flags, **calibConfig)
170 
171  toolDic[step] = tool
172  if tool.InScale == "JetConstitScaleMomentum":
173  foundCS = True
174 
175  if not foundCS:
176  raise JetCalibConfigError('At least one step must have InScale = JetConstitScaleMomentum')
177 
178  ordered_tools = []
179  ordered_step_names = []
180 
181  def findNextSteps(startScale = "JetConstitScaleMomentum"):
182  ''' Recursively add tools to ordered_tools based on in/out scale '''
183  for step in toolDic:
184  if toolDic[step].InScale == startScale:
185  ordered_tools.append(toolDic[step])
186  ordered_step_names.append(step)
187  findNextSteps(toolDic[step].OutScale)
188 
189  findNextSteps()
190 
191  # Check we've got all the steps
192  for step in toolDic:
193  if step not in ordered_step_names:
194  raise JetCalibConfigError(f'Could not place calib step {step} - have you set InScale and OutScale correctly?')
195 
196  jcslog.info(f'Ordered jet calib steps: {"->".join(ordered_step_names)}')
197 
198  return ordered_tools
199 
200 def calibToolFromConfigFile(flags, configFile, name = "jetcalib"):
201 
202  configDic = load_yaml_cfg(configFile)
203 
204  globalSettings = configDic.pop('Global',{})
205 
206  calibTool = CompFactory.JetCalibTool(name, CalibSteps=calibConfigToToolList(flags, **configDic), **globalSettings)
207  return calibTool
208 
209 def load_yaml_cfg(configFile):
210  from yaml import safe_load
211 
212  path_configFile = PathResolver.FindCalibFile(configFile)
213  configDic = safe_load(open(path_configFile))
214  return configDic
215 
216 
217 class JetCalibConfigError(Exception):
218  """ Exception raised for invalid jet calibration config """
219 
220  def __init__(self, message):
221  super().__init__(message)
222  self.message = message
223 
224  def __str__(self):
225  return self.message
PathResolver::FindCalibFile
static std::string FindCalibFile(const std::string &logical_file_name)
Definition: PathResolver.h:63
JetCalibStepsConfig.JetCalibConfigError.message
message
Definition: JetCalibStepsConfig.py:222
JetCalibStepsConfig.gscStep
def gscStep(flags, **configDict)
Definition: JetCalibStepsConfig.py:53
JetCalibStepsConfig.puresidualStep
def puresidualStep(flags, **configDict)
Definition: JetCalibStepsConfig.py:46
JetCalibStepsConfig.calibToolFromConfigFile
def calibToolFromConfigFile(flags, configFile, name="jetcalib")
Definition: JetCalibStepsConfig.py:200
JetCalibStepsConfig.calibConfigToToolList
def calibConfigToToolList(flags, **configDict)
Definition: JetCalibStepsConfig.py:141
JetCalibStepsConfig.insituStep
def insituStep(flags, **configDic)
Definition: JetCalibStepsConfig.py:109
JetCalibStepsConfig.JetCalibConfigError.__init__
def __init__(self, message)
Definition: JetCalibStepsConfig.py:220
JetCalibStepsConfig.JetCalibConfigError
Definition: JetCalibStepsConfig.py:217
JetCalibStepsConfig.JetCalibConfigError.__str__
def __str__(self)
Definition: JetCalibStepsConfig.py:224
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
HelperConfig.HistoInputCfg
def HistoInputCfg(flags, Tname, inputFile, histName, varX, **kwargs)
Definition: HelperConfig.py:31
HelperConfig.VarToolCfg
def VarToolCfg(flags, var, Tname="VarTool", **kwargs)
Definition: HelperConfig.py:4
Trk::open
@ open
Definition: BinningType.h:40
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:130
JetCalibStepsConfig.etajesStep
def etajesStep(flags, **configDic)
Definition: JetCalibStepsConfig.py:98
str
Definition: BTagTrackIpAccessor.cxx:11
JetCalibStepsConfig.smearingStep
def smearingStep(flags, **configDict)
Definition: JetCalibStepsConfig.py:13
JetCalibStepsConfig.load_yaml_cfg
def load_yaml_cfg(configFile)
Definition: JetCalibStepsConfig.py:209