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