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