Belle II Software  release-05-01-25
TOPCommonT0CalibratorModule.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/TOPCommonT0Calibrator/TOPCommonT0CalibratorModule.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(TOPCommonT0Calibrator)
44 
45  //-----------------------------------------------------------------
46  // Implementation
47  //-----------------------------------------------------------------
48 
50 
51  {
52  // set module description
53  setDescription("Common T0 calibration with dimuons or bhabhas.");
54  // setPropertyFlags(c_ParallelProcessingCertified);
55 
56  // Add parameters
57  addParam("numBins", m_numBins, "number of bins of the search region", 200);
58  addParam("timeRange", m_timeRange,
59  "time range in which to search for the minimum [ns]", 10.0);
60  addParam("minBkgPerBar", m_minBkgPerBar,
61  "minimal number of background photons per module", 0.0);
62  addParam("scaleN0", m_scaleN0,
63  "Scale factor for figure-of-merit N0", 1.0);
64  addParam("sigmaSmear", m_sigmaSmear,
65  "sigma in [ns] for additional smearing of PDF", 0.0);
66  addParam("sample", m_sample,
67  "sample type: one of dimuon, bhabha or cosmics", std::string("dimuon"));
68  addParam("minMomentum", m_minMomentum,
69  "minimal track momentum if sample is cosmics", 1.0);
70  addParam("deltaEcms", m_deltaEcms,
71  "c.m.s energy window (half size) if sample is dimuon or bhabha", 0.1);
72  addParam("dr", m_dr, "cut on POCA in r", 2.0);
73  addParam("dz", m_dz, "cut on POCA in abs(z)", 4.0);
74  addParam("minZ", m_minZ,
75  "minimal local z of extrapolated hit", -130.0);
76  addParam("maxZ", m_maxZ,
77  "maximal local z of extrapolated hit", 130.0);
78  addParam("outputFileName", m_outFileName,
79  "Root output file name containing calibration results. "
80  "File name can include *'s; "
81  "they will be replaced with a run number from the first input file",
82  std::string("commonT0_r*.root"));
83  addParam("pdfOption", m_pdfOption,
84  "PDF option, one of 'rough', 'fine', 'optimal'", std::string("rough"));
85 
86  }
87 
88  TOPCommonT0CalibratorModule::~TOPCommonT0CalibratorModule()
89  {
90  }
91 
92  void TOPCommonT0CalibratorModule::initialize()
93  {
94  // input collections
95  m_digits.isRequired();
96  m_tracks.isRequired();
97  m_extHits.isRequired();
98  m_recBunch.isOptional();
99 
100  // Configure TOP detector for reconstruction
101  TOPconfigure config;
102 
103  // bunch separation in time
104 
105  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
106  m_bunchTimeSep = geo->getNominalTDC().getSyncTimeBase() / 24;
107 
108  // Parse PDF option
109  if (m_pdfOption == "rough") {
110  m_PDFOption = TOPreco::c_Rough;
111  } else if (m_pdfOption == "fine") {
112  m_PDFOption = TOPreco::c_Fine;
113  } else if (m_pdfOption == "optimal") {
114  m_PDFOption = TOPreco::c_Optimal;
115  } else {
116  B2ERROR("TOPPDFDebuggerModule: unknown PDF option '" << m_pdfOption << "'");
117  }
118 
119  // set track selector
120  m_selector = TrackSelector(m_sample);
121  m_selector.setMinMomentum(m_minMomentum);
122  m_selector.setDeltaEcms(m_deltaEcms);
123  m_selector.setCutOnPOCA(m_dr, m_dz);
124  m_selector.setCutOnLocalZ(m_minZ, m_maxZ);
125 
126  // Chi2 minimum finders
127  double tmin = -m_timeRange / 2;
128  double tmax = m_timeRange / 2;
129  for (unsigned i = 0; i < c_numSets; i++) {
130  m_finders[i] = Chi2MinimumFinder1D(m_numBins, tmin, tmax);
131  }
132 
133  // if file name includes *'s replace them with a run number
134  auto pos = m_outFileName.find("*");
135  if (pos != std::string::npos) {
136  StoreObjPtr<EventMetaData> evtMetaData;
137  auto run = std::to_string(evtMetaData->getRun());
138  while (run.size() < 5) run = "0" + run;
139  while (pos != std::string::npos) {
140  m_outFileName.replace(pos, 1, run);
141  pos = m_outFileName.find("*");
142  }
143  }
144 
145  // open root file for ntuple and histogram output
146  m_file = TFile::Open(m_outFileName.c_str(), "RECREATE");
147  if (not m_file) {
148  B2ERROR("Cannot open output file '" << m_outFileName << "'");
149  return;
150  }
151 
152  // control histograms
153  m_hits1D = TH1F("numHits", "Number of photons per slot",
154  c_numModules, 0.5, c_numModules + 0.5);
155  m_hits1D.SetXTitle("slot number");
156  m_hits1D.SetYTitle("hits per slot");
157 
158  m_hits2D = TH2F("timeHits", "Photon times vs. boardstacks",
159  c_numModules * 4, 0.5, c_numModules + 0.5, 200, 0.0, 20.0);
160  m_hits2D.SetXTitle("slot number");
161  m_hits2D.SetYTitle("time [ns]");
162 
163  // create output tree
164  m_tree = new TTree("tree", "Channel T0 calibration results");
165  m_tree->Branch("slot", &m_moduleID);
166  m_tree->Branch("numPhotons", &m_numPhotons);
167  m_tree->Branch("x", &m_x);
168  m_tree->Branch("y", &m_y);
169  m_tree->Branch("z", &m_z);
170  m_tree->Branch("p", &m_p);
171  m_tree->Branch("theta", &m_theta);
172  m_tree->Branch("phi", &m_phi);
173  m_tree->Branch("r_poca", &m_pocaR);
174  m_tree->Branch("z_poca", &m_pocaZ);
175  m_tree->Branch("x_poca", &m_pocaX);
176  m_tree->Branch("y_poca", &m_pocaY);
177  m_tree->Branch("Ecms", &m_cmsE);
178  m_tree->Branch("charge", &m_charge);
179  m_tree->Branch("PDG", &m_PDG);
180 
181  }
182 
183  void TOPCommonT0CalibratorModule::beginRun()
184  {
185  }
186 
187  void TOPCommonT0CalibratorModule::event()
188  {
189  /* check bunch reconstruction status and run alignment:
190  - if object exists and bunch is found (collision data w/ bunch finder in the path)
191  - if object doesn't exist (cosmic data and other cases w/o bunch finder)
192  */
193 
194  if (m_recBunch.isValid()) {
195  if (!m_recBunch->isReconstructed()) return;
196  }
197 
198  // create reconstruction object and set various options
199 
200  int Nhyp = 1;
201  double mass = m_selector.getChargedStable().getMass();
202  int pdg = m_selector.getChargedStable().getPDGCode();
203  TOPreco reco(Nhyp, &mass, &pdg, m_minBkgPerBar, m_scaleN0);
204  reco.setPDFoption(m_PDFOption);
205  const auto& tdc = TOPGeometryPar::Instance()->getGeometry()->getNominalTDC();
206  double timeMin = tdc.getTimeMin();
207  double timeMax = tdc.getTimeMax();
208 
209  // add photon hits to reconstruction object
210 
211  for (const auto& digit : m_digits) {
212  if (digit.getHitQuality() == TOPDigit::c_Good)
213  reco.addData(digit.getModuleID(), digit.getPixelID(), digit.getTime(),
214  digit.getTimeError());
215  }
216 
217  // running offset must not be subtracted in TOPDigits: issue an error if it is
218 
219  if (isRunningOffsetSubtracted()) {
220  B2ERROR("Running offset subtracted in TOPDigits: common T0 will not be correct");
221  }
222 
223  // loop over reconstructed tracks, make a selection and accumulate log likelihoods
224 
225  for (const auto& track : m_tracks) {
226 
227  // track selection
228  TOPtrack trk(&track);
229  if (!trk.isValid()) continue;
230 
231  if (!m_selector.isSelected(trk)) continue;
232 
233  // run reconstruction
234  reco.reconstruct(trk);
235  if (reco.getFlag() != 1) continue; // track is not in the acceptance of TOP
236 
237  // minimization procedure: accumulate
238  int sub = gRandom->Integer(c_numSets); // generate sub-sample number
239  auto& finder = m_finders[sub];
240  const auto& binCenters = finder.getBinCenters();
241  for (unsigned ibin = 0; ibin < binCenters.size(); ibin++) {
242  double t0 = binCenters[ibin];
243  finder.add(ibin, -2 * reco.getLogL(t0, timeMin, timeMax, m_sigmaSmear));
244  }
245 
246  // fill histograms of hits
247  for (const auto& digit : m_digits) {
248  if (digit.getHitQuality() != TOPDigit::c_Good) continue;
249  if (digit.getModuleID() != trk.getModuleID()) continue;
250  if (digit.getTime() < timeMin) continue;
251  if (digit.getTime() > timeMax) continue;
252  m_hits1D.Fill(digit.getModuleID());
253  int bs = digit.getBoardstackNumber();
254  m_hits2D.Fill((digit.getModuleID() * 4 + bs - 1.5) / 4.0 , digit.getTime());
255  }
256 
257  // fill output tree
258  m_moduleID = trk.getModuleID();
259  m_numPhotons = reco.getNumOfPhotons();
260  const auto& localPosition = m_selector.getLocalPosition();
261  m_x = localPosition.X();
262  m_y = localPosition.Y();
263  m_z = localPosition.Z();
264  const auto& localMomentum = m_selector.getLocalMomentum();
265  m_p = localMomentum.Mag();
266  m_theta = localMomentum.Theta();
267  m_phi = localMomentum.Phi();
268  const auto& pocaPosition = m_selector.getPOCAPosition();
269  m_pocaR = pocaPosition.Perp();
270  m_pocaZ = pocaPosition.Z();
271  m_pocaX = pocaPosition.X();
272  m_pocaY = pocaPosition.Y();
273  m_cmsE = m_selector.getCMSEnergy();
274  m_charge = trk.getCharge();
275  m_PDG = trk.getPDGcode();
276  m_tree->Fill();
277  }
278 
279  }
280 
281 
282  void TOPCommonT0CalibratorModule::endRun()
283  {
284  }
285 
286  void TOPCommonT0CalibratorModule::terminate()
287  {
288 
289  // determine scaling factor for errors from statistically independent results
290 
291  TH1F h_pulls("pulls", "Pulls of statistically independent results",
292  200, -15.0, 15.0);
293  h_pulls.SetXTitle("pulls");
294  std::vector<double> pos, err;
295  for (int i = 0; i < c_numSets; i++) {
296  const auto& minimum = m_finders[i].getMinimum();
297  if (not minimum.valid) continue;
298  pos.push_back(minimum.position);
299  err.push_back(minimum.error);
300  }
301  for (unsigned i = 0; i < pos.size(); i++) {
302  for (unsigned j = i + 1; j < pos.size(); j++) {
303  double pull = (pos[i] - pos[j]) / sqrt(err[i] * err[i] + err[j] * err[j]);
304  h_pulls.Fill(pull);
305  }
306  }
307  h_pulls.Write();
308  double scaleError = 1;
309  if (h_pulls.GetEntries() > 1) scaleError = h_pulls.GetRMS();
310 
311  // merge statistically independent finders and store results into histograms
312 
313  auto finder = m_finders[0];
314  for (int i = 1; i < c_numSets; i++) {
315  finder.add(m_finders[i]);
316  }
317 
318  TH1F h_relCommonT0("relCommonT0", "relative common T0", 1, 0, 1);
319  h_relCommonT0.SetYTitle("common T0 residual [ns]");
320  TH1F h_commonT0("commonT0", "Common T0", 1, 0, 1);
321  h_commonT0.SetYTitle("common T0 [ns]");
322 
323  const auto& minimum = finder.getMinimum();
324  auto h = finder.getHistogram("chi2", "chi2");
325  h.Write();
326  if (minimum.valid) {
327  h_relCommonT0.SetBinContent(1, minimum.position);
328  h_relCommonT0.SetBinError(1, minimum.error * scaleError);
329  double T0 = minimum.position;
330  if (m_commonT0->isCalibrated()) T0 += m_commonT0->getT0();
331  T0 -= round(T0 / m_bunchTimeSep) * m_bunchTimeSep; // wrap around
332  h_commonT0.SetBinContent(1, T0);
333  h_commonT0.SetBinError(1, minimum.error * scaleError);
334  }
335  h_relCommonT0.Write();
336  h_commonT0.Write();
337 
338  // write other histograms and ntuple; close the file
339 
340  m_hits1D.Write();
341  m_hits2D.Write();
342  m_tree->Write();
343  m_file->Close();
344 
345  B2RESULT("Results available in " << m_outFileName);
346  }
347 
348  bool TOPCommonT0CalibratorModule::isRunningOffsetSubtracted()
349  {
350  for (const auto& digit : m_digits) {
351  if (digit.hasStatus(TOPDigit::c_BunchOffsetSubtracted)) return true;
352  }
353  return false;
354  }
355 
356 
358 } // end Belle2 namespace
359 
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::TOPCommonT0CalibratorModule
A module for common T0 calibration with collision data (dimuons or bhabhas)
Definition: TOPCommonT0CalibratorModule.h:51
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::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
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