Belle II Software  release-05-01-25
BoostVectorStandAlone.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2021 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Radek Zlebcik *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <iostream>
12 #include <vector>
13 #include <cmath>
14 #include <tuple>
15 #include <TTree.h>
16 #include <TVector3.h>
17 #include <Eigen/Dense>
18 
19 //if compiled within BASF2
20 #ifdef _PACKAGE_
21 #include <tracking/calibration/BoostVectorStandAlone.h>
22 #include <tracking/calibration/Splitter.h>
23 #include <tracking/calibration/tools.h>
24 #include <tracking/calibration/minimizer.h>
25 #else
26 #include <BoostVectorStandAlone.h>
27 #include <Splitter.h>
28 #include <tools.h>
29 #include <minimizer.h>
30 #endif
31 
32 using namespace std;
33 using Eigen::VectorXd;
34 using Eigen::Vector3d;
35 using Eigen::MatrixXd;
36 using Eigen::Matrix3d;
37 
38 namespace Belle2 {
44  namespace BoostVectorCalib {
45 
47  double median(double* v, int n)
48  {
49  B2ASSERT("At least 3 points to get median", n > 2);
50 
51  if (n % 2 == 1) { // odd number of elements
52  std::nth_element(v, v + n / 2, v + n);
53  return v[n / 2];
54  } else { //even
55  std::nth_element(v, v + n / 2, v + n);
56  double v1 = v[n / 2];
57 
58  std::nth_element(v, v + n / 2 - 1, v + n);
59  double v2 = v[n / 2 - 1];
60 
61  return (v1 + v2) / 2;
62  }
63  }
64 
65 
66 
67 
68 
69  // Read events from TTree to std::vector
70  vector<Event> getEvents(TTree* tr)
71  {
72 
73  vector<Event> events;
74  events.reserve(tr->GetEntries());
75 
76  Event evt;
77 
78  tr->SetBranchAddress("run", &evt.run);
79  tr->SetBranchAddress("exp", &evt.exp);
80  tr->SetBranchAddress("event", &evt.evtNo);
81 
82  TVector3* p0 = nullptr;
83  TVector3* p1 = nullptr;
84 
85  tr->SetBranchAddress("mu0_p", &p0);
86  tr->SetBranchAddress("mu1_p", &p1);
87 
88  tr->SetBranchAddress("mu0_pid", &evt.mu0.pid);
89  tr->SetBranchAddress("mu1_pid", &evt.mu1.pid);
90 
91 
92  tr->SetBranchAddress("time", &evt.t); //time in hours
93 
94 
95  for (int i = 0; i < tr->GetEntries(); ++i) {
96  tr->GetEntry(i);
97 
98  evt.mu0.p = *p0;
99  evt.mu1.p = *p1;
100 
101  evt.nBootStrap = 1;
102  evt.isSig = true;
103  events.push_back(evt);
104  }
105 
106  //sort by time
107  sort(events.begin(), events.end(), [](Event e1, Event e2) {return e1.t < e2.t;});
108 
109 
110  return events;
111  }
112 
114  struct FunBoost {
115  MatrixXd mat;
116  VectorXd res;
117 
119  double operator()(double c, double s)
120  {
121  // c and s are slopes of 2D linear function
122  Vector3d pars(-c, -s, -1);
123  res = mat * pars; // calculate residuals
124  res = res.array().square();
125 
126  return median(res.data(), res.rows());
127  }
128  };
129 
130 
132  MatrixXd toMat(const vector<vector<double>>& vecs)
133  {
134  MatrixXd mat(vecs[0].size(), vecs.size());
135 
136  for (unsigned k = 0; k < vecs.size(); ++k)
137  for (unsigned i = 0; i < vecs[k].size(); ++i)
138  mat(i, k) = vecs[k][i];
139  return mat;
140  }
141 
142 
144  vector<double> getRapidities(vector<TVector3> vecs, vector<double> boostDir)
145  {
146  TVector3 boost(boostDir[0], boostDir[1], 1);
147  boost = boost.Unit();
148 
149  double th0 = vecs[0].Angle(boost);
150  double th1 = vecs[1].Angle(boost);
151 
152  const double mL = 105.6583745e-3; //muon mass [GeV]
153 
154  double pMu0 = vecs[0].Mag();
155  double pMu1 = vecs[1].Mag();
156 
157  double C0 = 1. / sqrt(pow(mL / pMu0, 2) + 1);
158  double C1 = 1. / sqrt(pow(mL / pMu1, 2) + 1);
159 
160  double y0 = atanh(cos(th0) * C0);
161  double y1 = atanh(cos(th1) * C1);
162 
163  return {y0, y1};
164  }
165 
167  vector<double> fitBoostFast(const vector<Event>& evts)
168  {
169  vector<double> vCos, vSin, vData;
170 
171  for (const auto& e : evts) {
172  if (e.nBootStrap * e.isSig == 0) continue;
173  vector<TVector3> vecs = {e.mu0.p, e.mu1.p};
174 
175 
176  TVector3 n = vecs[0].Cross(vecs[1]);
177  double phi = n.Phi();
178  double angle = M_PI / 2 - n.Theta();
179  auto res = make_pair(phi, tan(angle));
180 
181  for (int i = 0; i < e.nBootStrap * e.isSig; ++i) {
182  vCos.push_back(cos(res.first));
183  vSin.push_back(sin(res.first));
184  vData.push_back(res.second);
185  }
186  }
187 
188 
189  FunBoost fun;
190 
191  fun.mat = toMat({vCos, vSin, vData});
192  fun.res.resize(vCos.size());
193 
194  auto res = getMinimum(fun, 130e-3 , 170e-3, -20e-3, 20e-3);
195 
196 
197  return res;
198  }
199 
200 
208  vector<Event> filter(const vector<Event>& evts, const vector<double>& boostDir, double pidCut, double rapCut)
209  {
210  vector<Event> evtsF;
211 
212  for (const auto& e : evts) {
213  for (int i = 0; i < e.nBootStrap * e.isSig; ++i) {
214 
215  //Keep only muons
216  if (!isnan(e.mu0.pid) && !isnan(e.mu0.pid)) {
217  if (e.mu0.pid < pidCut || e.mu1.pid < pidCut) {
218  continue;
219  }
220  }
221 
222  vector<TVector3> vecs = {e.mu0.p, e.mu1.p};
223  vector<double> raps = getRapidities(vecs, boostDir);
224 
225  double yDiff = abs(raps[0] - raps[1]) / 2;
226 
227  // Exclude events with fwd/bwd muons where det acc is limited
228  if (yDiff > rapCut) continue;
229 
230  evtsF.push_back(e);
231  }
232  }
233 
234  return evtsF;
235  }
236 
237 
238 
240  double fitBoostMagnitude(const vector<Event>& evts, const vector<double>& boostDir)
241  {
242 
243  vector<double> yAvgVec;
244 
245  for (auto e : evts) {
246  if (e.nBootStrap * e.isSig == 0) continue;
247  vector<TVector3> vecs = {e.mu0.p, e.mu1.p};
248 
249  vector<double> raps = getRapidities(vecs, boostDir);
250 
251  double yAvg = (raps[0] + raps[1]) / 2.;
252  for (int i = 0; i < e.nBootStrap * e.isSig; ++i) {
253  yAvgVec.push_back(yAvg);
254  }
255  }
256 
257  return median(yAvgVec.data(), yAvgVec.size());
258  }
259 
260 
262  struct vectorVar {
263  vector<Vector3d> vecs;
264 
266  void add(Vector3d v) { vecs.push_back(v); }
267 
269  Vector3d getNom() const { return vecs[0]; }
270 
272  MatrixXd getCov() const
273  {
274  Matrix3d res = Matrix3d::Zero();
275  for (unsigned i = 1; i < vecs.size(); ++i) {
276  Vector3d diff = vecs[i] - vecs[0];
277  res += diff * diff.transpose();
278  }
279  if (vecs.size() > 1)
280  res *= 1. / (vecs.size() - 1);
281  return res;
282 
283  }
284 
285 
286  };
287 
288 
290  TVector3 getBoostVector(const vector<Event>& evts)
291  {
292  // Get the direction of the boost vector (tanXZ and tanYZ)
293  vector<double> boostDir = fitBoostFast(evts);
294 
295  // Get the rapidity of the boost vector
296  double yMag = fitBoostMagnitude(evts, boostDir);
297 
298  //from rapidity to velocity
299  double beta = tanh(yMag);
300 
301  // Vector pointing in the boost vector direction
302  TVector3 bVec = TVector3(boostDir[0], boostDir[1], 1);
303 
304  // Adding proper magnitude of the vector (in speed of light units)
305  bVec = beta * bVec.Unit();
306 
307  return bVec;
308  }
309 
311  pair<Vector3d, Matrix3d> getBoostAndError(vector<Event> evts)
312  {
313  evts = filter(evts, {151.986e-3 /*TanNomAngle*/, 0}, 0.9/*muon pid*/, 1.0 /*rap cut*/);
314 
315  vectorVar var;
316 
317  const int nBoost = 10;
318  for (int i = 0; i < nBoost; ++i) {
319  if (i != 0)
320  for (auto& e : evts)
321  e.nBootStrap = gRandom->Poisson(1);
322 
323  TVector3 b = getBoostVector(evts);
324  Vector3d boost(b.X(), b.Y(), b.Z());
325  var.add(boost);
326  }
327  return make_pair(var.getNom(), var.getCov());
328 
329  }
330 
332  vector<vector<Event>> separate(const vector<Event>& evts, const vector<double>& splitPoints)
333  {
334  vector<vector<Event>> evtsOut(splitPoints.size() + 1);
335 
336  for (const auto& ev : evts) {
337  for (int i = 0; i < int(splitPoints.size()) - 1; ++i) {
338  if (splitPoints[i] < ev.t && ev.t < splitPoints[i + 1]) {
339  evtsOut[i + 1].push_back(ev);
340  break;
341  }
342  }
343 
344  if (splitPoints.size() >= 1) {
345  if (ev.t < splitPoints.front()) evtsOut.front().push_back(ev);
346  if (ev.t > splitPoints.back()) evtsOut.back().push_back(ev);
347  } else {
348  evtsOut[0].push_back(ev);
349  }
350  }
351  return evtsOut;
352  }
353 
354 
355 
356 
357 
358  // Returns tuple with the boost vector parameters
359  // cppcheck-suppress passedByValue
360  tuple<vector<VectorXd>, vector<MatrixXd>, MatrixXd> runBoostVectorAnalysis(vector<Event> evts,
361  const vector<double>& splitPoints)
362  {
363  int n = splitPoints.size() + 1;
364  vector<VectorXd> boostVec(n);
365  vector<MatrixXd> boostVecUnc(n);
366  MatrixXd boostVecSpred;
367 
368  vector<vector<Event>> evtsSep = separate(evts, splitPoints);
369 
370 
371  for (int i = 0; i < n; ++i) {
372 
373  tie(boostVec[i], boostVecUnc[i]) = getBoostAndError(evtsSep[i]);
374 
375 
376  // TanThetaXZ of boostVector [mrad]
377  double aX = 1e3 * boostVec[i][0] / boostVec[i][2];
378  // TanThetaYZ of boostVector [mrad]
379  double aY = 1e3 * boostVec[i][1] / boostVec[i][2];
380 
381  // TanThetaXZ unc of boostVector [mrad]
382  double eX = 1e3 * sqrt(boostVecUnc[i](0, 0)) / boostVec[i](2);
383  // TanThetaYZ unc of boostVector [mrad]
384  double eY = 1e3 * sqrt(boostVecUnc[i](1, 1)) / boostVec[i](2);
385 
386  B2INFO(evtsSep[i][0].run << " " << i << " " << evtsSep[i].size() << " : " << aX << "+-" << eX << " " << aY << "+-" << eY <<
387  " : " << 1e3 * boostVec[i].norm());
388  }
389 
390  // Spread is currently not calculated
391  boostVecSpred = MatrixXd::Zero(3, 3);
392 
393  return make_tuple(boostVec, boostVecUnc, boostVecSpred);
394  }
395 
396  }
398 }
Belle2::BoostVectorCalib::FunBoost::res
VectorXd res
vector with residuals^2
Definition: BoostVectorStandAlone.cc:116
Belle2::BoostVectorCalib::vectorVar::getNom
Vector3d getNom() const
Get the nominal result.
Definition: BoostVectorStandAlone.cc:269
VXDTFFilterTest::v2
const std::vector< double > v2
MATLAB generated random vector.
Definition: decorrelationMatrix.cc:44
prepareAsicCrosstalkSimDB.e
e
aux.
Definition: prepareAsicCrosstalkSimDB.py:53
Belle2::BoostVectorCalib::vectorVar
Structure to store all bootstrap replicas.
Definition: BoostVectorStandAlone.cc:262
VXDTFFilterTest::v1
const std::vector< double > v1
MATLAB generated random vector.
Definition: decorrelationMatrix.cc:30
Belle2::BoostVectorCalib::FunBoost::operator()
double operator()(double c, double s)
get median of residuals^2
Definition: BoostVectorStandAlone.cc:119
Belle2::filter
std::map< ExpRun, std::pair< double, double > > filter(const std::map< ExpRun, std::pair< double, double >> &runs, double cut, std::map< ExpRun, std::pair< double, double >> &runsRemoved)
filter events to remove runs shorter than cut, it stores removed runs in runsRemoved
Definition: Splitter.cc:43
Belle2::BoostVectorCalib::vectorVar::add
void add(Vector3d v)
Add replica.
Definition: BoostVectorStandAlone.cc:266
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::BoostVectorCalib::vectorVar::getCov
MatrixXd getCov() const
Get cov matrix with unc.
Definition: BoostVectorStandAlone.cc:272
Belle2::BoostVectorCalib::FunBoost
Functor to minimize median of residuals^2.
Definition: BoostVectorStandAlone.cc:114
Belle2::BoostVectorCalib::FunBoost::mat
MatrixXd mat
Matrix of the linear system.
Definition: BoostVectorStandAlone.cc:115
Belle2::BoostVectorCalib::vectorVar::vecs
vector< Vector3d > vecs
Vector of replicas.
Definition: BoostVectorStandAlone.cc:263