Skip to content

Commit 4dbe214

Browse files
committed
Merge pull request #869 in B2/basf2 from feature/BII-9294-include-beam-energy-calibration-in-airflow-for-proc13 to release/06-00
* commit '163a3fa17a0e57105d9d032c22baf8881100f9d9': Unifying name of cal and val scripts Adding vertical split lines on 4S/non-4S boundaries Adjusting the fitting range for non-4S according to median Removing white lines Different inital mumu fit parameters for on/off resonance Faster evaluation of the mumu fitted curve Adding mumu validation plots removing intNaN which is in reality 0 Add calibration tag btocharm_calib and update the caf_ecms.py script accordingly Merge pull request #468 in B2/basf2 from feature/BIIDP-3573-caf-scripts-for-beam-energy-calibration to main
2 parents 61e134e + 163a3fa commit 4dbe214

31 files changed

+6665
-2091
lines changed

calibration/scripts/prompt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"cosmic_calib": "cosmic_calib",
3333
"gamma_gamma_calib": "gamma_gamma_calib",
3434
"hadron_calib": "hadron_calib",
35+
"btocharm_calib": "btocharm_calib",
3536
"mumutight_or_highm_calib": "mumutight_or_highm_calib",
3637
"offip_calib": "offip_calib",
3738
"radmumu_calib": "radmumu_calib",
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
# -*- coding: utf-8 -*-
2+
3+
##########################################################################
4+
# basf2 (Belle II Analysis Software Framework) #
5+
# Author: The Belle II Collaboration #
6+
# #
7+
# See git log for contributors and copyright holders. #
8+
# This file is licensed under LGPL-3.0, see LICENSE.md. #
9+
##########################################################################
10+
11+
"""
12+
Airflow script to perform eCMS calibration (combination of the had-B and mumu method).
13+
"""
14+
15+
from prompt import CalibrationSettings, INPUT_DATA_FILTERS
16+
from prompt.calibrations.caf_boostvector import settings as boostvector
17+
from reconstruction import add_pid_module, add_ecl_modules, prepare_cdst_analysis
18+
19+
from basf2 import create_path, register_module, B2INFO
20+
import modularAnalysis as ma
21+
import vertex
22+
import stdCharged
23+
import stdPi0s
24+
25+
26+
#: Tells the automated system some details of this script
27+
settings = CalibrationSettings(
28+
name="Ecms Calibrations",
29+
expert_username="zlebcr",
30+
description=__doc__,
31+
input_data_formats=["cdst"],
32+
input_data_names=["hadron4S", "mumu4S", "mumuOff"],
33+
input_data_filters={
34+
"hadron4S": [
35+
INPUT_DATA_FILTERS["Data Tag"]["btocharm_calib"],
36+
INPUT_DATA_FILTERS["Run Type"]["physics"],
37+
INPUT_DATA_FILTERS["Beam Energy"]["4S"],
38+
INPUT_DATA_FILTERS["Data Quality Tag"]["Good Or Recoverable"],
39+
INPUT_DATA_FILTERS["Magnet"]["On"]],
40+
"mumu4S": [
41+
INPUT_DATA_FILTERS["Data Tag"]["mumutight_or_highm_calib"],
42+
INPUT_DATA_FILTERS["Run Type"]["physics"],
43+
INPUT_DATA_FILTERS["Beam Energy"]["4S"],
44+
INPUT_DATA_FILTERS["Data Quality Tag"]["Good Or Recoverable"],
45+
INPUT_DATA_FILTERS["Magnet"]["On"]],
46+
"mumuOff": [
47+
INPUT_DATA_FILTERS["Data Tag"]["mumutight_or_highm_calib"],
48+
INPUT_DATA_FILTERS["Run Type"]["physics"],
49+
INPUT_DATA_FILTERS["Beam Energy"]["Continuum"],
50+
INPUT_DATA_FILTERS['Beam Energy']['Scan'],
51+
INPUT_DATA_FILTERS["Data Quality Tag"]["Good Or Recoverable"],
52+
INPUT_DATA_FILTERS["Magnet"]["On"]]
53+
},
54+
expert_config={
55+
"outerLoss": "pow(0.000010e0*rawTime, 2) + 1./nEv",
56+
"innerLoss": "pow(0.000120e0*rawTime, 2) + 1./nEv",
57+
"runHadB": True,
58+
"eCMSmumuSpread": 5.2e-3,
59+
"eCMSmumuShift": 10e-3},
60+
depends_on=[boostvector])
61+
62+
##############################
63+
64+
65+
def get_hadB_path(isCDST):
66+
""" Selects the hadronic B decays, function returns corresponding path """
67+
68+
# module to be run prior the collector
69+
rec_path_1 = create_path()
70+
if isCDST:
71+
prepare_cdst_analysis(path=rec_path_1, components=['CDC', 'ECL', 'KLM'])
72+
73+
add_pid_module(rec_path_1)
74+
add_ecl_modules(rec_path_1)
75+
76+
stdCharged.stdPi(listtype='loose', path=rec_path_1)
77+
stdCharged.stdK(listtype='good', path=rec_path_1)
78+
stdPi0s.stdPi0s(listtype='eff40_May2020', path=rec_path_1)
79+
80+
ma.cutAndCopyList("pi+:my", "pi+:loose", "[abs(dz)<2.0] and [abs(dr)<0.5]", path=rec_path_1)
81+
ma.cutAndCopyList("K+:my", "K+:good", "[abs(dz)<2.0] and [abs(dr)<0.5]", path=rec_path_1)
82+
83+
ma.cutAndCopyList("pi0:my", "pi0:eff40_May2020", "", path=rec_path_1)
84+
85+
#####################################################
86+
# Reconstructs the signal B0 candidates from Dstar
87+
#####################################################
88+
89+
DcutLoose = '1.7 < M < 2.1'
90+
Dcut = '1.830 < M < 1.894'
91+
# Reconstructs D0s and sets decay mode identifiers
92+
ma.reconstructDecay(decayString='D0:Kpi -> K-:my pi+:my', cut=DcutLoose, dmID=1, path=rec_path_1)
93+
ma.reconstructDecay(decayString='D0:Kpipi0 -> K-:my pi+:my pi0:my',
94+
cut=DcutLoose, dmID=2, path=rec_path_1)
95+
ma.reconstructDecay(decayString='D0:Kpipipi -> K-:my pi+:my pi-:my pi+:my',
96+
cut=DcutLoose, dmID=3, path=rec_path_1)
97+
98+
# Performs mass constrained fit for all D0 candidates
99+
vertex.kFit(list_name='D0:Kpi', conf_level=0.0, fit_type='mass', path=rec_path_1)
100+
# vertex.kFit(list_name='D0:Kpipi0', conf_level=0.0, fit_type='mass', path=rec_path_1)
101+
vertex.kFit(list_name='D0:Kpipipi', conf_level=0.0, fit_type='mass', path=rec_path_1)
102+
103+
ma.applyCuts("D0:Kpi", Dcut, path=rec_path_1)
104+
ma.applyCuts("D0:Kpipi0", Dcut, path=rec_path_1)
105+
ma.applyCuts("D0:Kpipipi", Dcut, path=rec_path_1)
106+
107+
DStarcutLoose = 'massDifference(0) < 0.16'
108+
109+
# Reconstructs D*-s and sets decay mode identifiers
110+
ma.reconstructDecay(decayString='D*+:D0pi_Kpi -> D0:Kpi pi+:my', cut=DStarcutLoose, dmID=1, path=rec_path_1)
111+
ma.reconstructDecay(decayString='D*+:D0pi_Kpipi0 -> D0:Kpipi0 pi+:my',
112+
cut=DStarcutLoose, dmID=2, path=rec_path_1)
113+
ma.reconstructDecay(decayString='D*+:D0pi_Kpipipi -> D0:Kpipipi pi+:my',
114+
cut=DStarcutLoose, dmID=3, path=rec_path_1)
115+
116+
BcutLoose = '[ useCMSFrame(p) < 1.6 ] and [abs(dM) < 0.25]'
117+
Bcut = '[ useCMSFrame(p) < 1.2 ] and [abs(dM) < 0.05]'
118+
119+
# Reconstructs the signal B0 candidates from Dstar
120+
ma.reconstructDecay(decayString='B0:Dstpi_D0pi_Kpi -> D*-:D0pi_Kpi pi+:my',
121+
cut=BcutLoose,
122+
dmID=1, path=rec_path_1)
123+
ma.reconstructDecay(decayString='B0:Dstpi_D0pi_Kpipi0 -> D*-:D0pi_Kpipi0 pi+:my',
124+
cut=BcutLoose,
125+
dmID=2, path=rec_path_1)
126+
ma.reconstructDecay(decayString='B0:Dstpi_D0pi_Kpipipi -> D*-:D0pi_Kpipipi pi+:my',
127+
cut=BcutLoose,
128+
dmID=3, path=rec_path_1)
129+
130+
vertex.treeFit('B0:Dstpi_D0pi_Kpi', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
131+
vertex.treeFit('B0:Dstpi_D0pi_Kpipi0', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
132+
vertex.treeFit('B0:Dstpi_D0pi_Kpipipi', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
133+
134+
#####################################################
135+
# Reconstructs the signal B0 candidates from D-
136+
#####################################################
137+
138+
# Reconstructs charged D mesons and sets decay mode identifiers
139+
ma.reconstructDecay(decayString='D-:Kpipi -> K+:my pi-:my pi-:my',
140+
cut=DcutLoose, dmID=4, path=rec_path_1)
141+
142+
vertex.kFit(list_name='D-:Kpipi', conf_level=0.0, fit_type='mass', path=rec_path_1)
143+
ma.applyCuts("D-:Kpipi", '1.844 < M < 1.894', path=rec_path_1)
144+
145+
# Reconstructs the signal B candidates
146+
ma.reconstructDecay(decayString='B0:Dpi_Kpipi -> D-:Kpipi pi+:my',
147+
cut=BcutLoose, dmID=4, path=rec_path_1)
148+
149+
#####################################################
150+
# Reconstruct the signal B- candidates
151+
#####################################################
152+
153+
# Reconstructs the signal B- candidates
154+
ma.reconstructDecay(decayString='B-:D0pi_Kpi -> D0:Kpi pi-:my',
155+
cut=BcutLoose,
156+
dmID=5, path=rec_path_1)
157+
ma.reconstructDecay(decayString='B-:D0pi_Kpipi0 -> D0:Kpipi0 pi-:my',
158+
cut=BcutLoose,
159+
dmID=6, path=rec_path_1)
160+
ma.reconstructDecay(decayString='B-:D0pi_Kpipipi -> D0:Kpipipi pi-:my',
161+
cut=BcutLoose,
162+
dmID=7, path=rec_path_1)
163+
164+
vertex.treeFit('B-:D0pi_Kpi', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
165+
vertex.treeFit('B-:D0pi_Kpipi0', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
166+
vertex.treeFit('B-:D0pi_Kpipipi', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
167+
168+
ma.copyLists(
169+
outputListName='B0:merged',
170+
inputListNames=[
171+
'B0:Dstpi_D0pi_Kpi',
172+
'B0:Dstpi_D0pi_Kpipi0',
173+
'B0:Dstpi_D0pi_Kpipipi',
174+
'B0:Dpi_Kpipi'
175+
],
176+
path=rec_path_1)
177+
178+
ma.copyLists(
179+
outputListName='B-:merged',
180+
inputListNames=[
181+
'B-:D0pi_Kpi',
182+
'B-:D0pi_Kpipi0',
183+
'B-:D0pi_Kpipipi',
184+
],
185+
path=rec_path_1)
186+
187+
# Builds the rest of event object, which contains all particles not used in the reconstruction of B0 candidates.
188+
ma.buildRestOfEvent(target_list_name='B0:merged', path=rec_path_1)
189+
190+
# Calculates the continuum suppression variables
191+
cleanMask = ('cleanMask', 'nCDCHits > 0 and useCMSFrame(p)<=3.2', 'p >= 0.05 and useCMSFrame(p)<=3.2')
192+
ma.appendROEMasks(list_name='B0:merged', mask_tuples=[cleanMask], path=rec_path_1)
193+
ma.buildContinuumSuppression(list_name='B0:merged', roe_mask='cleanMask', path=rec_path_1)
194+
195+
# Builds the rest of event object, which contains all particles not used in the reconstruction of B- candidates.
196+
ma.buildRestOfEvent(target_list_name='B-:merged', path=rec_path_1)
197+
198+
# Calculates the continuum suppression variables
199+
cleanMask = ('cleanMask', 'nCDCHits > 0 and useCMSFrame(p)<=3.2', 'p >= 0.05 and useCMSFrame(p)<=3.2')
200+
ma.appendROEMasks(list_name='B-:merged', mask_tuples=[cleanMask], path=rec_path_1)
201+
ma.buildContinuumSuppression(list_name='B-:merged', roe_mask='cleanMask', path=rec_path_1)
202+
203+
ma.applyCuts("B0:merged", "[R2 < 0.3] and " + Bcut, path=rec_path_1)
204+
ma.applyCuts("B-:merged", "[R2 < 0.3] and " + Bcut, path=rec_path_1)
205+
206+
return rec_path_1
207+
208+
209+
def get_mumu_path(isCDST):
210+
""" Selects the ee -> mumu events, function returns corresponding path """
211+
212+
# module to be run prior the collector
213+
rec_path_1 = create_path()
214+
if isCDST:
215+
prepare_cdst_analysis(path=rec_path_1, components=['CDC', 'ECL', 'KLM'])
216+
217+
muSelection = '[p>1.0]'
218+
muSelection += ' and abs(dz)<2.0 and abs(dr)<0.5'
219+
muSelection += ' and nPXDHits >=1 and nSVDHits >= 8 and nCDCHits >= 20'
220+
221+
ma.fillParticleList('mu+:BV', muSelection, path=rec_path_1)
222+
ma.reconstructDecay('Upsilon(4S):BV -> mu+:BV mu-:BV', '9.5<M<11.5', path=rec_path_1)
223+
vertex.treeFit('Upsilon(4S):BV', updateAllDaughters=True, ipConstraint=True, path=rec_path_1)
224+
225+
return rec_path_1
226+
227+
228+
def get_data_info(inData, kwargs):
229+
""" Filter the input data and returns the IOVs """
230+
231+
# In this script we want to use one sources of input data.
232+
# Get the input files from the input_data variable
233+
file_to_iov_physics = inData
234+
235+
# We might have requested an enormous amount of data across a run range.
236+
# There's a LOT more files than runs!
237+
# Lets set some limits because this calibration doesn't need that much to run.
238+
max_files_per_run = 1000000
239+
240+
# We filter out any more than 100 files per run. The input data files are sorted alphabetically by b2caf-prompt-run
241+
# already. This procedure respects that ordering
242+
from prompt.utils import filter_by_max_files_per_run
243+
244+
reduced_file_to_iov_physics = filter_by_max_files_per_run(file_to_iov_physics, max_files_per_run)
245+
input_files_physics = list(reduced_file_to_iov_physics.keys())
246+
B2INFO(f"Total number of files actually used as input = {len(input_files_physics)}")
247+
248+
# Get the overall IoV we our process should cover. Includes the end values that we may want to ignore since our output
249+
# IoV should be open ended. We could also use this as part of the input data selection in some way.
250+
requested_iov = kwargs.get("requested_iov", None)
251+
252+
from caf.utils import IoV
253+
# The actual value our output IoV payload should have. Notice that we've set it open ended.
254+
output_iov = IoV(requested_iov.exp_low, requested_iov.run_low, -1, -1)
255+
256+
return input_files_physics, output_iov
257+
258+
259+
def get_calibrations(input_data, **kwargs):
260+
"""
261+
Required function used by b2caf-prompt-run tool.
262+
This function return a list of Calibration objects we assign to the CAF process.
263+
264+
Parameters:
265+
input_data (dict): Should contain every name from the 'input_data_names' variable as a key.
266+
Each value is a dictionary with {"/path/to/file_e1_r5.root": IoV(1,5,1,5), ...}. Useful for
267+
assigning to calibration.files_to_iov
268+
269+
**kwargs: Configuration options to be sent in. Since this may change we use kwargs as a way to help prevent
270+
backwards compatibility problems. But you could use the correct arguments in b2caf-prompt-run for this
271+
release explicitly if you want to.
272+
273+
Currently only kwargs["output_iov"] is used. This is the output IoV range that your payloads should
274+
correspond to. Generally your highest ExpRun payload should be open ended e.g. IoV(3,4,-1,-1)
275+
276+
Returns:
277+
list(caf.framework.Calibration): All of the calibration objects we want to assign to the CAF process
278+
"""
279+
280+
from caf.framework import Calibration
281+
from caf.strategies import SingleIOV
282+
283+
from ROOT.Belle2 import InvariantMassAlgorithm
284+
from caf.framework import Collection
285+
286+
input_files_Had, output_iov_Had = get_data_info(input_data["hadron4S"], kwargs)
287+
input_files_MuMu4S, output_iov_MuMu4S = get_data_info(input_data["mumu4S"], kwargs)
288+
input_files_MuMuOff, output_iov_MuMuOff = get_data_info(input_data["mumuOff"], kwargs)
289+
290+
isCDST = 'mdst' not in (input_files_MuMu4S + input_files_MuMuOff)[0]
291+
292+
rec_path_HadB = get_hadB_path(isCDST)
293+
rec_path_MuMu = get_mumu_path(isCDST)
294+
295+
collector_HadB = register_module('EcmsCollector')
296+
collector_MuMu = register_module('BoostVectorCollector', Y4SPListName='Upsilon(4S):BV')
297+
298+
algorithm_ecms = InvariantMassAlgorithm()
299+
algorithm_ecms.setOuterLoss(kwargs['expert_config']['outerLoss'])
300+
algorithm_ecms.setInnerLoss(kwargs['expert_config']['innerLoss'])
301+
302+
algorithm_ecms.includeHadBcalib(kwargs['expert_config']['runHadB'])
303+
algorithm_ecms.setMuMuEcmsSpread(kwargs['expert_config']['eCMSmumuSpread'])
304+
algorithm_ecms.setMuMuEcmsOffset(kwargs['expert_config']['eCMSmumuShift'])
305+
306+
calibration_ecms = Calibration('eCMS',
307+
algorithms=algorithm_ecms)
308+
309+
collection_HadB = Collection(collector=collector_HadB,
310+
input_files=input_files_Had,
311+
pre_collector_path=rec_path_HadB)
312+
collection_MuMu4S = Collection(collector=collector_MuMu,
313+
input_files=input_files_MuMu4S,
314+
pre_collector_path=rec_path_MuMu)
315+
collection_MuMuOff = Collection(collector=collector_MuMu,
316+
input_files=input_files_MuMuOff,
317+
pre_collector_path=rec_path_MuMu)
318+
319+
calibration_ecms.add_collection(name='dimuon_4S', collection=collection_MuMu4S)
320+
calibration_ecms.add_collection(name='dimuon_Off', collection=collection_MuMuOff)
321+
calibration_ecms.add_collection(name='hadB_4S', collection=collection_HadB)
322+
323+
calibration_ecms.strategies = SingleIOV
324+
# calibration_ecms.backend_args = {'extra_lines' : ["RequestRuntime = 6h"]}
325+
326+
# Do this for the default AlgorithmStrategy to force the output payload IoV
327+
# It may be different if you are using another strategy like SequentialRunByRun
328+
for algorithm in calibration_ecms.algorithms:
329+
algorithm.params = {"iov_coverage": output_iov_Had}
330+
331+
# Most other options like database chain and backend args will be overwritten by b2caf-prompt-run.
332+
# So we don't bother setting them.
333+
334+
# You must return all calibrations you want to run in the prompt process, even if it's only one
335+
return [calibration_ecms]
336+
337+
##############################

0 commit comments

Comments
 (0)