Belle II Software  release-06-02-00
TOPModuleT0CalibratorModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <top/modules/TOPModuleT0Calibrator/TOPModuleT0CalibratorModule.h>
10 #include <top/reconstruction_cpp/TOPTrack.h>
11 #include <top/reconstruction_cpp/PDFConstructor.h>
12 #include <top/reconstruction_cpp/TOPRecoManager.h>
13 #include <framework/dataobjects/EventMetaData.h>
14 #include <framework/logging/Logger.h>
15 #include <TRandom.h>
16 
17 using namespace std;
18 
19 namespace Belle2 {
24  using namespace TOP;
25 
26  //-----------------------------------------------------------------
27  // Register module
28  //-----------------------------------------------------------------
29 
30  REG_MODULE(TOPModuleT0Calibrator)
31 
32  //-----------------------------------------------------------------
33  // Implementation
34  //-----------------------------------------------------------------
35 
37 
38  {
39  // set module description
40  setDescription("Module T0 calibration with dimuons or bhabhas. "
41  "Useful when the geometrical alignment is fine.");
42  // setPropertyFlags(c_ParallelProcessingCertified);
43 
44  // Add parameters
45  addParam("numBins", m_numBins, "number of bins of the search region", 200);
46  addParam("timeRange", m_timeRange,
47  "time range in which to search for the minimum [ns]", 10.0);
48  addParam("sigmaSmear", m_sigmaSmear,
49  "sigma in [ns] for additional smearing of PDF", 0.0);
50  addParam("sample", m_sample,
51  "sample type: one of dimuon, bhabha or cosmics", std::string("dimuon"));
52  addParam("minMomentum", m_minMomentum,
53  "minimal track momentum if sample is cosmics", 1.0);
54  addParam("deltaEcms", m_deltaEcms,
55  "c.m.s energy window (half size) if sample is dimuon or bhabha", 0.1);
56  addParam("dr", m_dr, "cut on POCA in r", 2.0);
57  addParam("dz", m_dz, "cut on POCA in abs(z)", 4.0);
58  addParam("minZ", m_minZ,
59  "minimal local z of extrapolated hit", -130.0);
60  addParam("maxZ", m_maxZ,
61  "maximal local z of extrapolated hit", 130.0);
62  addParam("outputFileName", m_outFileName,
63  "Root output file name containing calibration results. "
64  "File name can include *'s; "
65  "they will be replaced with a run number from the first input file",
66  std::string("moduleT0_r*.root"));
67  addParam("pdfOption", m_pdfOption,
68  "PDF option, one of 'rough', 'fine', 'optimal'", std::string("rough"));
69 
70  }
71 
72  void TOPModuleT0CalibratorModule::initialize()
73  {
74  // input collections
75  m_digits.isRequired();
76  m_tracks.isRequired();
77  m_extHits.isRequired();
78  m_recBunch.isOptional();
79 
80  // toggle has changed status to prevent warning in beginRun for the first IOV
81  m_moduleT0.hasChanged();
82 
83  // Parse PDF option
84  if (m_pdfOption == "rough") {
85  m_PDFOption = PDFConstructor::c_Rough;
86  } else if (m_pdfOption == "fine") {
87  m_PDFOption = PDFConstructor::c_Fine;
88  } else if (m_pdfOption == "optimal") {
89  m_PDFOption = PDFConstructor::c_Optimal;
90  } else {
91  B2ERROR("TOPPDFDebuggerModule: unknown PDF option '" << m_pdfOption << "'");
92  }
93 
94  // set track selector
95  m_selector = TrackSelector(m_sample);
96  m_selector.setMinMomentum(m_minMomentum);
97  m_selector.setDeltaEcms(m_deltaEcms);
98  m_selector.setCutOnPOCA(m_dr, m_dz);
99  m_selector.setCutOnLocalZ(m_minZ, m_maxZ);
100 
101  // Chi2 minimum finders
102  double tmin = -m_timeRange / 2;
103  double tmax = m_timeRange / 2;
104  for (unsigned i = 0; i < 2; i++) {
105  for (unsigned m = 0; m < c_numModules; m++) {
106  m_finders[i][m] = Chi2MinimumFinder1D(m_numBins, tmin, tmax);
107  }
108  }
109 
110  // if file name includes *'s replace them with a run number
111  auto pos = m_outFileName.find("*");
112  if (pos != std::string::npos) {
113  StoreObjPtr<EventMetaData> evtMetaData;
114  auto run = std::to_string(evtMetaData->getRun());
115  while (run.size() < 5) run = "0" + run;
116  while (pos != std::string::npos) {
117  m_outFileName.replace(pos, 1, run);
118  pos = m_outFileName.find("*");
119  }
120  }
121 
122  // open root file for ntuple and histogram output
123  m_file = TFile::Open(m_outFileName.c_str(), "RECREATE");
124  if (not m_file) {
125  B2ERROR("Cannot open output file '" << m_outFileName << "'");
126  return;
127  }
128 
129  // histograms
130  m_hits1D = TH1F("numHits", "Number of photons per slot",
131  c_numModules, 0.5, c_numModules + 0.5);
132  m_hits1D.SetXTitle("slot number");
133  m_hits1D.SetYTitle("hits per slot");
134 
135  m_hits2D = TH2F("timeHits", "Photon times vs. boardstacks",
136  c_numModules * 4, 0.5, c_numModules + 0.5, 200, 0.0, 20.0);
137  m_hits2D.SetXTitle("slot number");
138  m_hits2D.SetYTitle("time [ns]");
139 
140  // create output tree
141  m_tree = new TTree("tree", "Channel T0 calibration results");
142  m_tree->Branch("slot", &m_moduleID);
143  m_tree->Branch("numPhotons", &m_numPhotons);
144  m_tree->Branch("x", &m_x);
145  m_tree->Branch("y", &m_y);
146  m_tree->Branch("z", &m_z);
147  m_tree->Branch("p", &m_p);
148  m_tree->Branch("theta", &m_theta);
149  m_tree->Branch("phi", &m_phi);
150  m_tree->Branch("r_poca", &m_pocaR);
151  m_tree->Branch("z_poca", &m_pocaZ);
152  m_tree->Branch("x_poca", &m_pocaX);
153  m_tree->Branch("y_poca", &m_pocaY);
154  m_tree->Branch("Ecms", &m_cmsE);
155  m_tree->Branch("charge", &m_charge);
156  m_tree->Branch("PDG", &m_PDG);
157 
158  }
159 
160  void TOPModuleT0CalibratorModule::beginRun()
161  {
162  if (m_moduleT0.hasChanged()) {
163  StoreObjPtr<EventMetaData> evtMetaData;
164  B2WARNING("ModuleT0 payload has changed. Calibration results may not be correct."
165  << LogVar("run", evtMetaData->getRun()));
166  }
167  }
168 
169  void TOPModuleT0CalibratorModule::event()
170  {
171  /* check bunch reconstruction status and run alignment:
172  - if object exists and bunch is found (collision data w/ bunch finder in the path)
173  - if object doesn't exist (cosmic data and other cases w/o bunch finder)
174  */
175 
176  if (m_recBunch.isValid()) {
177  if (not m_recBunch->isReconstructed()) return;
178  }
179 
180  TOPRecoManager::setDefaultTimeWindow();
181  double timeMin = TOPRecoManager::getMinTime();
182  double timeMax = TOPRecoManager::getMaxTime();
183 
184  // running offset must not be subtracted in TOPDigits: issue an error if it is
185 
186  if (isRunningOffsetSubtracted()) {
187  B2ERROR("Running offset subtracted in TOPDigits: module T0 will not be correct");
188  }
189 
190  // loop over reconstructed tracks, make a selection and accumulate log likelihoods
191 
192  for (const auto& track : m_tracks) {
193 
194  // track selection
195  TOPTrack trk(track);
196  if (not trk.isValid()) continue;
197 
198  if (not m_selector.isSelected(trk)) continue;
199 
200  // construct PDF
201  PDFConstructor pdfConstructor(trk, m_selector.getChargedStable(), m_PDFOption);
202  if (not pdfConstructor.isValid()) continue;
203 
204  // minimization procedure: accumulate
205  const unsigned module = trk.getModuleID() - 1;
206  if (module >= c_numModules) continue;
207  int sub = gRandom->Integer(2); // generate sub-sample number
208  auto& finder = m_finders[sub][module];
209  const auto& binCenters = finder.getBinCenters();
210  for (unsigned ibin = 0; ibin < binCenters.size(); ibin++) {
211  double t0 = binCenters[ibin];
212  finder.add(ibin, -2 * pdfConstructor.getLogL(t0, m_sigmaSmear).logL);
213  }
214 
215  // fill histograms of hits
216  m_numPhotons = 0;
217  for (const auto& digit : m_digits) {
218  if (digit.getHitQuality() != TOPDigit::c_Good) continue;
219  if (digit.getModuleID() != trk.getModuleID()) continue;
220  if (digit.getTime() < timeMin) continue;
221  if (digit.getTime() > timeMax) continue;
222  m_numPhotons++;
223  m_hits1D.Fill(digit.getModuleID());
224  int bs = digit.getBoardstackNumber();
225  m_hits2D.Fill((digit.getModuleID() * 4 + bs - 1.5) / 4.0 , digit.getTime());
226  }
227 
228  // fill output tree
229  m_moduleID = trk.getModuleID();
230  const auto& localPosition = m_selector.getLocalPosition();
231  m_x = localPosition.X();
232  m_y = localPosition.Y();
233  m_z = localPosition.Z();
234  const auto& localMomentum = m_selector.getLocalMomentum();
235  m_p = localMomentum.Mag();
236  m_theta = localMomentum.Theta();
237  m_phi = localMomentum.Phi();
238  const auto& pocaPosition = m_selector.getPOCAPosition();
239  m_pocaR = pocaPosition.Perp();
240  m_pocaZ = pocaPosition.Z();
241  m_pocaX = pocaPosition.X();
242  m_pocaY = pocaPosition.Y();
243  m_cmsE = m_selector.getCMSEnergy();
244  m_charge = trk.getCharge();
245  m_PDG = trk.getPDGCode();
246  m_tree->Fill();
247  }
248 
249  }
250 
251 
252  void TOPModuleT0CalibratorModule::terminate()
253  {
254  // determine scaling factor for errors from two statistically independent results
255 
256  TH1F h_pulls("pulls", "Pulls of the two statistically independent results",
257  200, -15.0, 15.0);
258  h_pulls.SetXTitle("pulls");
259  for (unsigned module = 0; module < c_numModules; module++) {
260  std::vector<double> pos, err;
261  for (int i = 0; i < 2; i++) {
262  const auto& minimum = m_finders[i][module].getMinimum();
263  if (not minimum.valid) continue;
264  pos.push_back(minimum.position);
265  err.push_back(minimum.error);
266  }
267  if (pos.size() < 2) continue;
268  double pull = (pos[0] - pos[1]) / sqrt(err[0] * err[0] + err[1] * err[1]);
269  h_pulls.Fill(pull);
270  }
271  h_pulls.Write();
272  double scaleError = h_pulls.GetRMS();
273 
274  // merge two statistically independent finders and store results into histograms
275 
276  TH1F h_relModuleT0("relModuleT0", "Module T0 relative to calibration",
277  c_numModules, 0.5, c_numModules + 0.5);
278  h_relModuleT0.SetXTitle("slot number");
279  h_relModuleT0.SetYTitle("module T0 residual [ns]");
280 
281  for (unsigned module = 0; module < c_numModules; module++) {
282  auto& finder = m_finders[0][module].add(m_finders[1][module]);
283  const auto& minimum = finder.getMinimum();
284  if (minimum.valid) {
285  h_relModuleT0.SetBinContent(module + 1, minimum.position);
286  h_relModuleT0.SetBinError(module + 1, minimum.error * scaleError);
287  }
288  std::string slotNum = to_string(module + 1);
289  auto h = finder.getHistogram("chi2_slot_" + slotNum, "slot " + slotNum);
290  h.Write();
291  }
292  h_relModuleT0.Write();
293 
294  // absolute module T0
295  TH1F h_moduleT0("moduleT0", "Module T0",
296  c_numModules, 0.5, c_numModules + 0.5);
297  h_moduleT0.SetXTitle("slot number");
298  h_moduleT0.SetYTitle("module T0 [ns]");
299 
300  double average = 0; // average to be subtracted
301  int num = 0;
302  for (int slot = 1; slot <= c_numModules; slot++) {
303  if (h_relModuleT0.GetBinError(slot) > 0) {
304  double T0 = 0;
305  if (m_moduleT0->isCalibrated(slot)) T0 = m_moduleT0->getT0(slot);
306  average += h_relModuleT0.GetBinContent(slot) + T0;
307  num++;
308  }
309  }
310  if (num > 0) average /= num;
311 
312  for (int slot = 1; slot <= c_numModules; slot++) {
313  double T0 = 0;
314  if (m_moduleT0->isCalibrated(slot)) T0 = m_moduleT0->getT0(slot);
315  double t0 = h_relModuleT0.GetBinContent(slot) + T0 - average;
316  double err = h_relModuleT0.GetBinError(slot);
317  h_moduleT0.SetBinContent(slot, t0);
318  h_moduleT0.SetBinError(slot, err);
319  }
320  h_moduleT0.Write();
321 
322  // write other histograms and ntuple; close the file
323 
324  m_hits1D.Write();
325  m_hits2D.Write();
326  m_tree->Write();
327  m_file->Close();
328 
329  B2RESULT(num << "/16 calibrated. Results available in " << m_outFileName);
330  }
331 
332 
333  bool TOPModuleT0CalibratorModule::isRunningOffsetSubtracted()
334  {
335  for (const auto& digit : m_digits) {
336  if (digit.hasStatus(TOPDigit::c_BunchOffsetSubtracted)) return true;
337  }
338  return false;
339  }
340 
342 } // end Belle2 namespace
343 
Base class for Modules.
Definition: Module.h:72
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
A module for module T0 calibration with collision data (dimuons or bhabhas) Useful when the geometric...
Minimum finder using tabulated chi^2 values in one dimension.
PDF construction and log likelihood determination for a given track and particle hypothesis.
LogL getLogL() const
Returns extended log likelihood (using the default time window)
bool isValid() const
Checks the object status.
Reconstructed track at TOP.
Definition: TOPTrack.h:40
int getPDGCode() const
Returns PDG code of associated MCParticle (returns 0 if none)
Definition: TOPTrack.h:230
double getCharge() const
Returns charge.
Definition: TOPTrack.h:163
bool isValid() const
Checks if track is successfully constructed.
Definition: TOPTrack.h:139
int getModuleID() const
Returns slot ID.
Definition: TOPTrack.h:145
Utility for the track selection - used in various calibration modules.
Definition: TrackSelector.h:26
Class to store variables with their name which were sent to the logging service.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.
double logL
extended log likelihood