Belle II Software  release-05-01-25
vxdcdc_alignment.py
1 # -*- coding: utf-8 -*-
2 
3 """
4 
5 Simultaneous Global and Local VXD and CDC (layers-only) alignment with Millepede II
6 
7 The input collections can be (only single tracks currently):
8 - cosmics (hlt skim) - mandatorry
9 - physics - all raw data -> once off-ip is available, this can be omitted
10 - hadron - for "low" momentum tracks from IP
11 - mumu - mumu_2trk or mumu_tight - for high momentum tracks from IP
12 - offip - not yet available - tracks from outside IP (beam background, beam-gas)
13 
14 Time-dependence can be (manually) configured for VXD half-shells and CDC layers.
15 For example to allow VXD alignment to change in run 10, 20 an 30 in experiment 12, you set:
16 
17 >>> timedep_vxd : [[0, 10, 12], [0, 20, 12], [0, 30, 12]]
18 
19 Note that the first run in your requested iov will be added automatically.
20 
21 """
22 
23 from prompt import CalibrationSettings
24 from prompt.calibrations.caf_cdc import settings as cdc_calibration
25 
26 collection_names = ["physics", "cosmic", "hadron", "mumu", "offip"]
27 
28 default_config = {
29  'max_iterations': 3,
30  'min_entries': 1000000,
31 
32  'method': 'diagonalization 3 0.1',
33  'scaleerrors': 1.,
34  'entries': 100,
35 
36  'minPValue': 0.00001,
37 
38  "physics.min_events": 400000,
39  "physics.max_processed_events_per_file": 2000,
40 
41  "cosmic.min_events": 1000000,
42  "cosmic.max_processed_events_per_file": 5000,
43 
44  "hadron.min_events": 100000,
45  "hadron.max_processed_events_per_file": 1000,
46 
47  "mumu.min_events": 400000,
48  "mumu.max_processed_events_per_file": 3000,
49 
50  "offip.min_events": 400000,
51  "offip.max_processed_events_per_file": 2000,
52 
53  "timedep_vxd": [],
54  "timedep_cdc": []
55  }
56 
57 
58 settings = CalibrationSettings(name="VXD and CDC Alignment",
59  expert_username="bilkat",
60  description=__doc__,
61  input_data_formats=["raw"],
62  input_data_names=collection_names,
63  expert_config=default_config,
64  depends_on=[cdc_calibration])
65 
66 
67 def select_files(all_input_files, min_events, max_processed_events_per_file):
68  import basf2
69  from random import choice
70  from prompt.utils import events_in_basf2_file
71  # Let's iterate, taking a sample of files from the total (no repeats or replacement) until we get enough events
72  total_events = 0
73  chosen_files = []
74  while total_events < min_events:
75  # If the set is empty we must have used all available files. Here we break and continue. But you may want to
76  # raise an Error...
77  if not all_input_files:
78  break
79  # Randomly select a file
80  new_file_choice = choice(all_input_files)
81  # Remove it from the list so it can't be chosen again
82  all_input_files.remove(new_file_choice)
83  # Find the number of events in the file
84  total_events_in_file = events_in_basf2_file(new_file_choice)
85  if not total_events_in_file:
86  # Uh Oh! Zero event file, skip it
87  continue
88 
89  events_contributed = min(total_events_in_file, max_processed_events_per_file)
90 
91  chosen_files.append(new_file_choice)
92  total_events += events_contributed
93 
94  basf2.B2INFO(f"Total chosen files = {len(chosen_files)}")
95  basf2.B2INFO(f"Total events in chosen files = {total_events}")
96  if total_events < min_events:
97  basf2.B2FATAL(
98  f"There weren't enough files events selected when max_processed_events_per_file={max_processed_events_per_file}")
99  return chosen_files
100 
101 
102 def create_std_path():
103  import basf2
104  import rawdata as raw
105  import reconstruction as reco
106 
107  path = basf2.create_path()
108  path.add_module('Progress')
109  path.add_module('RootInput')
110  path.add_module('Gearbox')
111  path.add_module('Geometry')
112  raw.add_unpackers(path)
113  path.add_module('SetupGenfitExtrapolation')
114  reco.add_reconstruction(
115  path,
116  pruneTracks=False,
117  skipGeometryAdding=True,
118  )
119  path.add_module('DAFRecoFitter')
120  return path
121 
122 
123 def create_cosmics_path():
124  import basf2
125  import rawdata as raw
126  import reconstruction as reco
127  import modularAnalysis as ana
128 
129  path = basf2.create_path()
130  path.add_module('Progress')
131  # Remove all non-raw data to run the full reco again
132  path.add_module('RootInput')
133  path.add_module('Gearbox')
134  path.add_module('Geometry')
135  path.add_module(
136  "TriggerSkim",
137  triggerLines=["software_trigger_cut&filter&cosmic"]).if_value(
138  "==0",
139  basf2.Path(),
140  basf2.AfterConditionPath.END)
141 
142  raw.add_unpackers(path)
143  path.add_module('SetupGenfitExtrapolation')
144  reco.add_cosmics_reconstruction(
145  path,
146  pruneTracks=False,
147  skipGeometryAdding=True,
148  addClusterExpertModules=False,
149  data_taking_period='early_phase3',
150  merge_tracks=True
151  )
152 
153  path.add_module('SetRecoTrackMomentum', automatic=True)
154  path.add_module('DAFRecoFitter', pdgCodesToUseForFitting=[13])
155 
156  ana.fillParticleList(
157  'mu+:goodForVXDCDCAlignment',
158  '[z0 <= 57. or abs(d0) >= 26.5] and abs(dz) > 0.4 and nTracks == 1',
159  path=path)
160  path.add_module('SkimFilter', particleLists=['mu+:goodForVXDCDCAlignment']).if_false(basf2.create_path())
161 
162  return path
163 
164 
167 
168 
169 def get_calibrations(input_data, **kwargs):
170 
171  import basf2
172 
173  from caf.utils import IoV
174 
175  import millepede_calibration as mpc
176 
177  import alignment.constraints
178  import alignment.parameters
179 
180  from random import seed
181  seed(1234)
182 
183  cfg = kwargs['expert_config']
184  files = dict()
185 
186  for colname in collection_names:
187  file_to_iov = input_data[colname]
188  input_files = list(file_to_iov.keys())
189 
190  if not len(input_files):
191  files[colname] = []
192  continue
193 
194  basf2.B2INFO(f"Selecting files for: {colname}")
195  input_files = select_files(input_files[:], cfg[f'{colname}.min_events'], cfg[f'{colname}.max_processed_events_per_file'])
196  files[colname] = input_files
197 
198  # Get the overall IoV we want to cover for this request, including the end values
199  requested_iov = kwargs.get("requested_iov", None)
200  # The actual value our output IoV payload should have. Notice that we've set it open ended.
201  output_iov = IoV(requested_iov.exp_low, requested_iov.run_low, -1, -1)
202 
203  # Pede command options
204  method = cfg['method']
205  scaleerrors = cfg['scaleerrors']
206  entries = cfg['entries']
207 
208  timedep = []
209 
210  timedep_vxd = cfg['timedep_vxd']
211  timedep_cdc = cfg['timedep_cdc']
212 
213  if len(timedep_vxd):
214  slices = [(erx[0], erx[1], erx[2]) for erx in timedep_vxd] + [(0, requested_iov.run_low, requested_iov.exp_low)]
215  timedep.append(
217  if len(timedep_cdc):
218  slices = [(erx[0], erx[1], erx[2]) for erx in timedep_cdc] + [(0, requested_iov.run_low, requested_iov.exp_low)]
219  timedep.append((alignment.parameters.cdc_layers(), slices))
220 
221  cal = mpc.create(
222  name='VXDCDCalignment',
223  dbobjects=['VXDAlignment', 'CDCAlignment'],
224  collections=[
225  mpc.make_collection("cosmic", path=create_cosmics_path(), tracks=["RecoTracks"]),
226  mpc.make_collection("physics", path=create_std_path(), tracks=["RecoTracks"]),
227  mpc.make_collection("hadron", path=create_std_path(), tracks=["RecoTracks"]),
228  mpc.make_collection("mumu", path=create_std_path(), tracks=["RecoTracks"]),
229  mpc.make_collection("offip", path=create_std_path(), tracks=["RecoTracks"])
230  ],
231  tags=None,
232  files=files,
233  timedep=timedep,
234  constraints=[
235  alignment.constraints.VXDHierarchyConstraints(type=2, pxd=True, svd=True),
236  alignment.constraints.CDCLayerConstraints(z_offset=True, z_scale=False)
237  ],
238  fixed=alignment.parameters.vxd_sensors(rigid=False, surface2=False, surface3=False, surface4=True),
239  commands=[
240  f"method {method}",
241  f"scaleerrors {scaleerrors} {scaleerrors}",
242  f"entries {entries}"],
243  params=dict(minPValue=cfg['minPValue'], externalIterations=0),
244  min_entries=cfg['min_entries'])
245 
246  for colname in collection_names:
247  max_processed_events_per_file = cfg[f'{colname}.max_processed_events_per_file']
248  basf2.set_module_parameters(
249  cal.collections[colname].pre_collector_path,
250  'RootInput',
251  entrySequences=[f'0:{max_processed_events_per_file}'])
252 
253  # Bugfix for Condor:
254  from alignment.prompt_utils import fix_mille_paths_for_algo
255  fix_mille_paths_for_algo(cal.algorithms[0])
256 
257  # Most values like database chain and backend args are overwritten by b2caf-prompt-run. But some can be set.
258  cal.max_iterations = cfg['max_iterations']
259 
260  # Force the output payload IoV to be correct.
261  # It may be different if you are using another strategy like SequentialRunByRun so we ask you to set this up correctly.
262  for algorithm in cal.algorithms:
263  algorithm.params = {"apply_iov": output_iov}
264 
265  return [cal]
prompt.utils
Definition: utils.py:1
alignment.parameters.cdc_layers
def cdc_layers(layers=None)
Definition: parameters.py:4
alignment.parameters.vxd_halfshells
def vxd_halfshells(pxd=True, svd=True, parameters=None, ying=True, yang=True, pat=True, mat=True)
Definition: parameters.py:81
alignment.parameters.vxd_sensors
def vxd_sensors(layers=None, rigid=True, surface=True, surface2=True, surface3=True, surface4=True, parameters=None)
Definition: parameters.py:146
alignment.prompt_utils
Definition: prompt_utils.py:1
alignment.constraints.CDCLayerConstraints
Definition: constraints.py:167
alignment.constraints.VXDHierarchyConstraints
Definition: constraints.py:115
alignment.constraints
Definition: constraints.py:1
alignment.parameters
Definition: parameters.py:1