Belle II Software  release-05-01-25
HitBasedT0Extractor.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2017 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Thomas Hauth *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 #include <tracking/eventTimeExtraction/findlets/HitBasedT0Extractor.h>
11 #include <tracking/eventTimeExtraction/findlets/BaseEventTimeExtractor.icc.h>
12 
13 #include <tracking/trackFindingCDC/utilities/StringManipulation.h>
14 
15 #include <framework/core/ModuleParamList.h>
16 
17 #include <framework/logging/Logger.h>
18 #include <framework/utilities/ScopeGuard.h>
19 
20 #include <TH1D.h>
21 #include <TF1.h>
22 #include <TFitResult.h>
23 #include <TCanvas.h>
24 
25 #include <boost/lexical_cast.hpp>
26 
27 #include <memory>
28 
29 using namespace Belle2;
30 using namespace TrackFindingCDC;
31 
33 {
34  return "Extracts the T0 time of an event only using CDC Hits";
35 }
36 
38 {
39  m_eventMetaData.isRequired();
40  Super::initialize();
41 }
42 
44  const std::string& prefix)
45 {
46  moduleParamList->addParameter(prefixed(prefix, "searchWindow"),
47  m_param_searchWindow,
48  "Size of the time distance (in ns) to search for t0 in both direction from the current best t0",
49  m_param_searchWindow);
50 
51  moduleParamList->addParameter(prefixed(prefix, "fitWindow"),
52  m_param_fitWindow,
53  "Size of the time distance (in ns) to used for fitting",
54  m_param_fitWindow);
55 
56  moduleParamList->addParameter(prefixed(prefix, "refitWindow"),
57  m_param_refitWindow,
58  "Size of the time distance (in ns) to used for re-fitting",
59  m_param_refitWindow);
60 
61  moduleParamList->addParameter(prefixed(prefix, "binCountTimeHistogram"),
62  m_param_binCountTimeHistogram,
63  "Number of bins in the timing histogram used for fitting",
64  m_param_binCountTimeHistogram);
65 
66  moduleParamList->addParameter(prefixed(prefix, "rejectByBackgroundFlag"),
67  m_param_rejectByBackgroundFlag,
68  "Don't consider hits if they have the background flag set",
69  m_param_rejectByBackgroundFlag);
70 
71  moduleParamList->addParameter(prefixed(prefix, "rejectIfNotTakenFlag"),
72  m_param_rejectIfNotTakenFlag,
73  "Don't consider hits which have not been assigned during track finding. The CDC track finding has "
74  "to be run before for this flag to be useful.",
75  m_param_rejectIfNotTakenFlag);
76 
77  moduleParamList->addParameter(prefixed(prefix, "rejectIfChiSquareLargerThan"),
78  m_param_rejectIfChiSquareLargerThan,
79  "consider the t0 fit failed which have larger chi2 than this number",
80  m_param_rejectIfChiSquareLargerThan);
81 
82  moduleParamList->addParameter(prefixed(prefix, "rejectIfUncertaintyLargerThan"),
83  m_param_rejectIfUncertaintyLargerThan,
84  "consider the t0 fit if the uncertainty on t0 is larger than this value",
85  m_param_rejectIfUncertaintyLargerThan);
86 
87  moduleParamList->addParameter(prefixed(prefix, "storeAllFits"),
88  m_param_storeAllFits,
89  "store images for all fits",
90  m_param_storeAllFits);
91 
92  moduleParamList->addParameter(prefixed(prefix, "minHitCount"),
93  m_param_minHitCount,
94  "Minimum amount of hits which is required to try the extraction",
95  m_param_minHitCount);
96 
97  Super::exposeParameters(moduleParamList, prefix);
98 }
99 
100 void HitBasedT0Extractor::apply(std::vector<CDCWireHit>& inputWireHits)
101 {
102  const auto timeHistogramName = "HitBasedT0Extractor_time_hist";
103  const std::string debugImageName = "HitBasedT0Extractor_debug_" + boost::lexical_cast<std::string>
104  (m_eventMetaData->getEvent()) + ".png";
105 
106  auto timingHistgram = TH1D(timeHistogramName, timeHistogramName,
107  m_param_binCountTimeHistogram, -m_param_fitWindow,
108  m_param_fitWindow);
109 
110  // Enable batch mode - we do not want to show the canvas etc.
111  auto batchGuard = ScopeGuard::guardBatchMode();
112 
113  TCanvas canvas(debugImageName.c_str(), debugImageName.c_str(), 800, 600);
114 
115  if (inputWireHits.size() == 0) {
116  B2WARNING("No input CDC hits available for the CDC hit based t0 extraction.");
117  return;
118  }
119 
120  for (auto const& wireHit : inputWireHits) {
121 
122  if (m_param_rejectByBackgroundFlag
123  && wireHit.getAutomatonCell().hasBackgroundFlag())
124  continue;
125 
126  if (m_param_rejectIfNotTakenFlag
127  && !wireHit.getAutomatonCell().hasTakenFlag())
128  continue;
129 
130  // the taken flag is also set for background hits. Therefore we must
131  // also hits to be also classified as background to not get flooded
132  // with background hits
133  if (m_param_rejectIfNotTakenFlag
134  && wireHit.getAutomatonCell().hasTakenFlag()
135  && wireHit.getAutomatonCell().hasBackgroundFlag())
136  continue;
137 
138  // Attention: at this stage we use the drift time that was set when the WireHits were created.
139  // We will just *assume* that it was zero at this time!
140  timingHistgram.Fill(wireHit.getDriftTime());
141  }
142 
143  if (timingHistgram.GetEntries() < m_param_minHitCount) {
144  B2DEBUG(50, "Only " << timingHistgram.GetEntries() << " hits satisfied the requirements for t0 extraction, " << m_param_minHitCount
145  << " are required.");
146  return;
147  }
148 
149  // add an overall offset of 1 to not have to care about empty bins in
150  // the cumulated plot
151  timingHistgram.SetBinContent(1, timingHistgram.GetBinContent(1) + 1);
152 
153  B2DEBUG(50,
154  "Filled histogram with " << timingHistgram.GetEntries() << " Entries");
155 
156  std::unique_ptr<TH1> cumTimingHistogram(timingHistgram.GetCumulative());
157  // detach histogram from any directory so we can delete it ourselves
158  cumTimingHistogram->SetDirectory(0);
159 
160  // set the error for each bin taking into account that the bin content is the
161  // cumulated content of previous bins
162  double errPrevBin = 0.0f;
163  double errThisBin = 0.0f;
164  for (int i = 0; i < cumTimingHistogram->GetNbinsX(); i++) {
165  if (i > 0) {
166  const double prevEntries = cumTimingHistogram->GetBinContent(i - 1);
167  const double addedEntries = cumTimingHistogram->GetBinContent(i) - prevEntries;
168  if (addedEntries > 0.0) {
169  // combine the error of the previous bin with the new entries of this bin
170  const double errNewEntries = 1.0 / std::sqrt(addedEntries);
171  errThisBin = errPrevBin + errNewEntries;
172  }
173 
174  } else {
175  if (cumTimingHistogram->GetBinContent(i) > 0) {
176  errThisBin = 1.0 / std::sqrt(cumTimingHistogram->GetBinContent(i));
177  }
178  }
179 
180  cumTimingHistogram->SetBinError(i, errThisBin);
181  errPrevBin = errThisBin;
182  }
183 
184  if (m_param_storeAllFits) {
185  cumTimingHistogram->Draw();
186  }
187 
188  auto rangeBkg = std::make_pair(-m_param_fitWindow, -m_param_searchWindow);
189  auto rangeSig = std::make_pair(m_param_searchWindow, m_param_fitWindow);
190  // fit the background and signal side of the time distribution
191  TF1 fitfuncBkg = TF1("HitBasedT0Extractor_fit_bkg", "[0]*x + [1]",
192  rangeBkg.first, rangeBkg.second);
193  TF1 fitfuncSig = TF1("HitBasedT0Extractor_fit_sig", "[0]*x + [1]",
194  rangeSig.first, rangeSig.second);
195 
196  auto fitresBkg = cumTimingHistogram->Fit(&fitfuncBkg, "LQS", "",
197  rangeBkg.first, rangeBkg.second);
198  auto fitresSig = cumTimingHistogram->Fit(&fitfuncSig, "LQS", "",
199  rangeSig.first, rangeSig.second);
200 
201  if (m_param_storeAllFits) {
202  fitfuncBkg.Draw("SAME");
203  fitfuncSig.Draw("SAME");
204  }
205 
206  TF1 fitfuncSegmented = TF1("HitBasedT0Extractor_fit_seg",
207  "[1]*(x+TMath::Abs(x+[3])) + [2]*(x-TMath::Abs(x+[3])) + [0]",
208  -m_param_fitWindow, m_param_fitWindow);
209 
210  if (fitresSig->IsValid() && fitresBkg->IsValid()) {
211  double t0_estimate = (fitresBkg->Parameter(1) - fitresSig->Parameter(1))
212  / (fitresSig->Parameter(0) - fitresBkg->Parameter(0));
213 
214  // apply segmented fit
215  std::array<double, 4> fit_params = { 0, // = overall background offset
216  fitresSig->Parameter(0), // signal hits slope
217  fitresBkg->Parameter(0), // background hits slope
218  0.0 // breaking point shift
219  };
220 
221  // use t0 estimate if it is something useful
222  if (std::abs(t0_estimate) < m_param_searchWindow) {
223  fit_params[3] = t0_estimate;
224  }
225 
226  fitfuncSegmented.SetParameters(fit_params.data());
227 
228  auto fitresFull = cumTimingHistogram->Fit(&fitfuncSegmented, "QS", "", -m_param_fitWindow, m_param_fitWindow);
229  if (m_param_storeAllFits) {
230  fitfuncSegmented.Draw("SAME");
231  }
232 
233  // refit with a fixed window around the extracted t0 to remove a possible bias
234  // because we fitted a large part of the signal side in the first iteration
235  const double fitted_t0_first = -fitresFull->Parameter(3);
236  bool refitSuccess = false;
237  if (std::abs(fitted_t0_first) < m_param_searchWindow) {
238  fitresFull = cumTimingHistogram->Fit(&fitfuncSegmented, "QS", "",
239  fitted_t0_first - m_param_refitWindow, fitted_t0_first + m_param_refitWindow);
240  refitSuccess = true;
241  } else {
242  B2DEBUG(50, "First t0 estimate not in proper range" << fitted_t0_first);
243  }
244 
245  if (m_param_storeAllFits) {
246  fitfuncSegmented.Draw("SAME");
247  }
248 
249  // actually store this result ?
250  if (refitSuccess && fitresFull->IsValid()) {
251  const double fitted_t0 = -fitresFull->Parameter(3);
252  const double fitted_t0_error = fitresFull->Error(3);
253 
254  const double norm_chi2 = fitresFull->Chi2() / double(fitresFull->Ndf());
255 
256  B2DEBUG(50, "T0 fit with t0 " << fitted_t0 << " +- " << fitted_t0_error << " and normalized chi2 " << norm_chi2 << " and " <<
257  timingHistgram.GetEntries() << " hits");
258 
259  // check if all the criteria required for a "good fit" have been met
260  if (norm_chi2 > m_param_rejectIfChiSquareLargerThan) {
261  B2DEBUG(50,
262  "T0 fit has too large Chi2 " << fitresFull->Chi2());
263  } else if (std::abs(fitted_t0_error) > m_param_rejectIfUncertaintyLargerThan) {
264  B2DEBUG(50,
265  "T0 fit has too large error " << fitted_t0_error);
266  } else {
267 
268  // Since drift times are corrected for EventT0 (in RealisticTDCCountTranslator), if any other T0 modules were executed before, add the EventT0 offset back.
269  // This leads to "absolute" event T0 determination which should be consistent with other T0 modules.
270  double lastEventT0 = m_eventT0->hasEventT0() ? m_eventT0->getEventT0() : 0;
271  EventT0::EventT0Component eventT0Component(fitted_t0 + lastEventT0, fitted_t0_error, Const::CDC, "hit based", norm_chi2);
272  m_eventT0->addTemporaryEventT0(eventT0Component);
273  m_eventT0->setEventT0(eventT0Component);
274  m_wasSuccessful = true;
275  B2DEBUG(50,
276  "Successful t0 extraction with CDC hits: " << fitted_t0 << " +- " << fitted_t0_error);
277  }
278  } else {
279  B2DEBUG(50,
280  "Cannot fit t0 from CDC hits only. Won't set EventT0 for now.");
281  }
282  } else {
283  B2DEBUG(50,
284  "Cannot extract background or signal segment because fit failed. Won't set EventT0 for now.");
285  }
286 
287  if (m_param_storeAllFits) {
288  canvas.Draw();
289  canvas.SaveAs(debugImageName.c_str());
290  }
291 }
Belle2::ScopeGuard::guardBatchMode
static ScopeGuard guardBatchMode(bool batchMode=true)
Create a ScopeGuard to turn ROOT into batch mode and restore the initial batch mode status after the ...
Definition: ScopeGuard.h:362
Belle2::EventT0::EventT0Component
Structure for storing the extracted event t0s together with its detector and its uncertainty.
Definition: EventT0.h:44
Belle2::HitBasedT0Extractor::initialize
void initialize() override final
Initialize the event meta data.
Definition: HitBasedT0Extractor.cc:37
Belle2::HitBasedT0Extractor::exposeParameters
void exposeParameters(ModuleParamList *moduleParamList, const std::string &prefix) override final
Expose the parameters to a module.
Definition: HitBasedT0Extractor.cc:43
Belle2::ModuleParamList::addParameter
void addParameter(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module list.
Definition: ModuleParamList.templateDetails.h:38
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::HitBasedT0Extractor::apply
void apply(std::vector< TrackFindingCDC::CDCWireHit > &inputWireHits) override final
Collects all Wire Hits and executes the t0 fit.
Definition: HitBasedT0Extractor.cc:100
Belle2::HitBasedT0Extractor::getDescription
std::string getDescription() override final
Short description of the findlet.
Definition: HitBasedT0Extractor.cc:32
Belle2::ModuleParamList
The Module parameter list class.
Definition: ModuleParamList.h:46