Belle II Software development
AxialHitQuadTreeProcessor.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#include <tracking/trackFindingCDC/legendre/quadtree/AxialHitQuadTreeProcessor.h>
9
10#include <tracking/trackingUtilities/eventdata/hits/CDCWireHit.h>
11#include <tracking/trackingUtilities/geometry/VectorUtil.h>
12
13#include <array>
14#include <vector>
15
16#include <Math/Vector2D.h>
17#include <TF1.h>
18#include <TCanvas.h>
19#include <TGraph.h>
20#include <TAxis.h>
21
22using namespace Belle2;
23using namespace TrackFindingCDC;
24using namespace TrackingUtilities;
25
26namespace {
27 bool sameSign(double n1, double n2, double n3, double n4)
28 {
29 return ((n1 > 0 && n2 > 0 && n3 > 0 && n4 > 0) || (n1 < 0 && n2 < 0 && n3 < 0 && n4 < 0));
30 }
31
32 using YSpan = AxialHitQuadTreeProcessor::YSpan;
33 YSpan splitCurvSpan(const YSpan& curvSpan, int nodeLevel, int lastLevel, int j)
34 {
35 const float meanCurv = curvSpan[0] + (curvSpan[1] - curvSpan[0]) / 2.0;
36 const std::array<float, 3> binBounds{curvSpan[0], meanCurv, curvSpan[1]};
37 const float binWidth = binBounds[j + 1] - binBounds[j];
38
39 const bool standardBinning = (nodeLevel <= lastLevel - 7) or (std::fabs(meanCurv) <= 0.005);
40 if (standardBinning) {
41 // B2INFO("Case 1* " << meanCurv << " " << (meanCurv <= 0.005));
42 float curv1 = binBounds[j];
43 float curv2 = binBounds[j + 1];
44
45 // Standard bin division
46 return {curv1, curv2};
47 }
48
49 // Non-standard binning
50 // For level 6 to 7 only expand 1 / 4, for higher levels expand 1 / 8.
51 // (assuming last level == 12)
52 if (nodeLevel < lastLevel - 5) {
53 // B2INFO("Case 2*");
54 float curv1 = binBounds[j] - binWidth / 4.;
55 float curv2 = binBounds[j + 1] + binWidth / 4.;
56 return {curv1, curv2};
57 } else {
58 // B2INFO("Case 3*");
59 float curv1 = binBounds[j] - binWidth / 8.;
60 float curv2 = binBounds[j + 1] + binWidth / 8.;
61 return {curv1, curv2};
62 }
63 }
64
65}
66
67std::vector<float> AxialHitQuadTreeProcessor::createCurvBound(YSpan curvSpan, int lastLevel)
68{
69 std::vector<YSpan> spans{{curvSpan}};
70
71 std::vector<YSpan> nextSpans;
72 for (int level = 1; level <= lastLevel; ++level) {
73 nextSpans.clear();
74 for (const YSpan& span : spans) {
75 nextSpans.push_back(splitCurvSpan(span, level, lastLevel, 0));
76 nextSpans.push_back(splitCurvSpan(span, level, lastLevel, 1));
77 }
78 spans.swap(nextSpans);
79 }
80
81 std::vector<float> bounds;
82 for (const YSpan& span : spans) {
83 bounds.push_back(span[0]);
84 bounds.push_back(span[1]);
85 }
86
87 assert(bounds.size() == std::pow(2, lastLevel));
88 return bounds;
89}
90
92{
93 static const int maxLevel = PrecisionUtil::getLookupGridLevel();
94 static const int nBins = std::pow(2, maxLevel);
95 static LookupTable<ROOT::Math::XYVector> trigonometricLookUpTable(&VectorUtil::Phi, nBins, -M_PI, M_PI);
96 return trigonometricLookUpTable;
97}
98
100 int seedLevel,
101 const XYSpans& ranges,
102 PrecisionUtil::PrecisionFunction precisionFunction)
103 : QuadTreeProcessor(lastLevel, seedLevel, ranges)
104 , m_precisionFunction(precisionFunction)
105 , m_localOrigin(0.0, 0.0)
107{
108 m_twoSidedPhaseSpace = m_quadTree->getYMin() * m_quadTree->getYMax() < 0;
109}
110
111AxialHitQuadTreeProcessor::AxialHitQuadTreeProcessor(const ROOT::Math::XYVector& localOrigin,
112 const YSpan& curvSpan,
113 const LookupTable<ROOT::Math::XYVector>* cosSinLookupTable)
114 : QuadTreeProcessor(0, 0, { {0, cosSinLookupTable->getNPoints() - 1}, curvSpan})
115, m_localOrigin(localOrigin)
116, m_cosSinLookupTable(cosSinLookupTable)
117{
118 // Never use two sided mode in off origin extension
119 m_twoSidedPhaseSpace = false;
120}
121
122
124{
125 if (node->getLevel() <= 6) return false;
126 if (node->getLevel() >= getLastLevel()) return true;
127
128 const double nodeResolution = fabs(node->getYMin() - node->getYMax());
129 const double meanCurv = (node->getYMax() + node->getYMin()) / 2;
130
131 const double resolution = m_precisionFunction(meanCurv);
132 if (resolution >= nodeResolution) return true;
133
134 return false;
135}
136
139{
140 const int nodeLevel = node->getLevel();
141 const int lastLevel = getLastLevel();
142 const float meanCurv = std::fabs(node->getYMax() + node->getYMin()) / 2;
143
144 // Expand bins for all nodes 7 levels before the last level (for lastLevel = 12 starting at 6)
145 // but only in a curvature region higher than 0.005. Lower than that use always standard.
146 bool standardBinning = (nodeLevel <= lastLevel - 7) or (meanCurv <= 0.005);
147
148 if (standardBinning) {
149 float r1 = node->getYLowerBound(j);
150 float r2 = node->getYUpperBound(j);
151 long theta1 = node->getXLowerBound(i);
152 long theta2 = node->getXUpperBound(i);
153
154 // Standard bin division
155 return XYSpans({theta1, theta2}, {r1, r2});
156 }
157
158 // Non-standard binning
159 // For level 6 to 7 only expand 1 / 4, for higher levels expand 1 / 8.
160 // (assuming last level == 12)
161 if (nodeLevel < lastLevel - 5) {
162 float r1 = node->getYLowerBound(j) - node->getYBinWidth(j) / 4.;
163 float r2 = node->getYUpperBound(j) + node->getYBinWidth(j) / 4.;
164
165 // long extension = pow(2, lastLevel - nodeLevel) / 4; is same as:
166 long extension = pow(2, lastLevel - nodeLevel - 2);
167
168 long theta1 = node->getXLowerBound(i) - extension;
169 if (theta1 < 0) theta1 = 0;
170
171 long theta2 = node->getXUpperBound(i) + extension;
172 if (theta2 >= m_cosSinLookupTable->getNPoints()) {
173 theta2 = m_cosSinLookupTable->getNPoints() - 1;
174 }
175
176 return XYSpans({theta1, theta2}, {r1, r2});
177 } else {
178 float r1 = node->getYLowerBound(j) - node->getYBinWidth(j) / 8.;
179 float r2 = node->getYUpperBound(j) + node->getYBinWidth(j) / 8.;
180
181 // long extension = pow(2, lastLevel - nodeLevel) / 8; is same as
182 long extension = pow(2, lastLevel - nodeLevel - 3);
183
184 long theta1 = node->getXLowerBound(i) - extension;
185 if (theta1 < 0) theta1 = 0;
186
187 long theta2 = node->getXUpperBound(i) + extension;
188 if (theta2 >= m_cosSinLookupTable->getNPoints()) {
189 theta2 = m_cosSinLookupTable->getNPoints() - 1;
190 }
191
192 return XYSpans({theta1, theta2}, {r1, r2});
193 }
194}
195
197{
198 // Check whether the hit lies in the forward direction
199 if (node->getLevel() <= 4 and m_twoSidedPhaseSpace and node->getYMin() > -c_curlCurv and
200 node->getYMax() < c_curlCurv) {
201 if (not checkDerivative(node, wireHit)) return false;
202 }
203
204 const double& l = wireHit->getRefDriftLength();
205 const ROOT::Math::XYVector& pos2D = wireHit->getRefPos2D() - m_localOrigin;
206 double r2 = pos2D.Mag2() - l * l;
207
208 using Quadlet = std::array<std::array<float, 2>, 2>;
209 Quadlet distRight{};
210 Quadlet distLeft{};
211
212 // get top and bottom borders of the node
213 float rMin = node->getYMin() * r2 / 2;
214 float rMax = node->getYMax() * r2 / 2;
215
216 // get left and right borders of the node
217 long thetaMin = node->getXMin();
218 long thetaMax = node->getXMax();
219
220 const ROOT::Math::XYVector& thetaVecMin = m_cosSinLookupTable->at(thetaMin);
221 const ROOT::Math::XYVector& thetaVecMax = m_cosSinLookupTable->at(thetaMax);
222
223 float rHitMin = thetaVecMin.Dot(pos2D);
224 float rHitMax = thetaVecMax.Dot(pos2D);
225
226 // compute sinograms at the left and right borders of the node
227 float rHitMinRight = rHitMin - l;
228 float rHitMaxRight = rHitMax - l;
229
230 float rHitMinLeft = rHitMin + l;
231 float rHitMaxLeft = rHitMax + l;
232
233 // Compute distance from the sinograms to bottom and top borders of the node
234 distRight[0][0] = rMin - rHitMinRight;
235 distRight[0][1] = rMin - rHitMaxRight;
236 distRight[1][0] = rMax - rHitMinRight;
237 distRight[1][1] = rMax - rHitMaxRight;
238
239 distLeft[0][0] = rMin - rHitMinLeft;
240 distLeft[0][1] = rMin - rHitMaxLeft;
241 distLeft[1][0] = rMax - rHitMinLeft;
242 distLeft[1][1] = rMax - rHitMaxLeft;
243
244 // Compare distance signs from sinograms to the node
245 // Check right
246 if (not sameSign(distRight[0][0], distRight[0][1], distRight[1][0], distRight[1][1])) {
247 return true;
248 }
249
250 // Check left
251 if (not sameSign(distLeft[0][0], distLeft[0][1], distLeft[1][0], distLeft[1][1])) {
252 return true;
253 }
254
255 // Check the extremum
256 float rHitMinExtr = VectorUtil::Cross(thetaVecMin, pos2D);
257 float rHitMaxExtr = VectorUtil::Cross(thetaVecMax, pos2D);
258 if (rHitMinExtr * rHitMaxExtr < 0.) return checkExtremum(node, wireHit);
259
260 // Not contained
261 return false;
262}
263
265{
266 const ROOT::Math::XYVector& pos2D = wireHit->getRefPos2D() - m_localOrigin;
267
268 long thetaMin = node->getXMin();
269 long thetaMax = node->getXMax();
270
271 const ROOT::Math::XYVector& thetaVecMin = m_cosSinLookupTable->at(thetaMin);
272 const ROOT::Math::XYVector& thetaVecMax = m_cosSinLookupTable->at(thetaMax);
273
274 float rMinD = VectorUtil::Cross(thetaVecMin, pos2D);
275 float rMaxD = VectorUtil::Cross(thetaVecMax, pos2D);
276
277 // Does not really make sense...
278 if ((rMinD > 0) && (rMaxD * rMinD >= 0)) return true;
279 if ((rMaxD * rMinD < 0)) return true;
280 return false;
281}
282
284{
285 const double& l = wireHit->getRefDriftLength();
286 const ROOT::Math::XYVector& pos2D = wireHit->getRefPos2D() - m_localOrigin;
287 double r2 = pos2D.Mag2() - l * l;
288
289 // get left and right borders of the node
290 long thetaMin = node->getXMin();
291 long thetaMax = node->getXMax();
292
293 const ROOT::Math::XYVector& thetaVecMin = m_cosSinLookupTable->at(thetaMin);
294 const ROOT::Math::XYVector& thetaVecMax = m_cosSinLookupTable->at(thetaMax);
295
296 if (not VectorUtil::isBetween(pos2D, thetaVecMin, thetaVecMax)) return false;
297
298 // compute sinograms at the position
299 double r = pos2D.R();
300 float rRight = r - l;
301 float rLeft = r + l;
302
303 // get top and bottom borders of the node
304 float rMin = node->getYMin() * r2 / 2;
305 float rMax = node->getYMax() * r2 / 2;
306
307 bool crossesRight = (rMin - rRight) * (rMax - rRight) < 0;
308 bool crossesLeft = (rMin - rLeft) * (rMax - rLeft) < 0;
309 return crossesRight or crossesLeft;
310}
311
312void AxialHitQuadTreeProcessor::drawHits(std::vector<const CDCWireHit*> hits, unsigned int color) const
313{
314 static int nevent(0);
315
316 TCanvas* canv = new TCanvas("canv", "legendre transform", 0, 0, 1200, 600);
317 canv->cd(1);
318 TGraph* dummyGraph = new TGraph();
319 dummyGraph->SetPoint(1, -M_PI, 0);
320 dummyGraph->SetPoint(2, M_PI, 0);
321 dummyGraph->Draw("AP");
322 dummyGraph->GetXaxis()->SetTitle("#theta");
323 dummyGraph->GetYaxis()->SetTitle("#rho");
324 dummyGraph->GetXaxis()->SetRangeUser(-M_PI, M_PI);
325 dummyGraph->GetYaxis()->SetRangeUser(-0.02, 0.15);
326
327 for (const CDCWireHit* wireHit : hits) {
328 const double& l = wireHit->getRefDriftLength();
329 const ROOT::Math::XYVector& pos2D = wireHit->getRefPos2D() - m_localOrigin;
330 double x = pos2D.x();
331 double y = pos2D.y();
332 double r2 = pos2D.Mag2() - l * l;
333
334 TF1* concaveHitLegendre = new TF1("concaveHitLegendre", "2*([0]/[3])*cos(x) + 2*([1]/[3])*sin(x) + 2*([2]/[3])", -M_PI, M_PI);
335 TF1* convexHitLegendre = new TF1("convexHitLegendre", "2*([0]/[3])*cos(x) + 2*([1]/[3])*sin(x) - 2*([2]/[3])", -M_PI, M_PI);
336 concaveHitLegendre->SetLineWidth(1);
337 convexHitLegendre->SetLineWidth(1);
338 concaveHitLegendre->SetLineColor(color);
339 convexHitLegendre->SetLineColor(color);
340
341 concaveHitLegendre->SetParameters(x, y, l, r2);
342 convexHitLegendre->SetParameters(x, y, l, r2);
343 concaveHitLegendre->Draw("CSAME");
344 convexHitLegendre->Draw("CSAME");
345 }
346// canv->Print(Form("legendreHits_%i.root", nevent));
347// canv->Print(Form("legendreHits_%i.eps", nevent));
348 canv->Print(Form("legendreHits_%i.png", nevent));
349 delete canv;
350
351 nevent++;
352}
353
355{
356 std::vector<const CDCWireHit*> hits;
357 for (Item* item : node->getItems()) {
358 const CDCWireHit* wireHit = item->getPointer();
359 hits.push_back(wireHit);
360 }
361 drawHits(hits);
362}
XYSpans createChild(QuadTree *node, int i, int j) const final
Return the new ranges.
ROOT::Math::XYVector m_localOrigin
Local origin on which the phase space coordinates are centered.
const double c_curlCurv
The curvature above which the trajectory is considered a curler.
const TrackingUtilities::LookupTable< ROOT::Math::XYVector > * m_cosSinLookupTable
Pinned lookup table for precomputed cosine and sine values.
bool m_twoSidedPhaseSpace
Indicator whether the two sided phases space insertion check should be used This option should automa...
void drawHits(std::vector< const TrackingUtilities::CDCWireHit * > hits, unsigned int color=46) const
Draw QuadTree node.
bool checkDerivative(QuadTree *node, const TrackingUtilities::CDCWireHit *wireHit) const
Check derivative of the sinogram.
bool isLeaf(QuadTree *node) const final
lastLevel depends on curvature of the track candidate
bool isInNode(QuadTree *node, const TrackingUtilities::CDCWireHit *wireHit) const final
Check whether hit belongs to the quadtree node:
bool checkExtremum(QuadTree *node, const TrackingUtilities::CDCWireHit *wireHit) const
Checks whether extreme point is located within QuadTree node's ranges.
AxialHitQuadTreeProcessor(int lastLevel, int seedLevel, const XYSpans &ranges, PrecisionUtil::PrecisionFunction precisionFunction)
Constructor.
PrecisionUtil::PrecisionFunction m_precisionFunction
Lambda which holds resolution function for the quadtree.
void drawNode(QuadTree *node) const
Draw QuadTree node.
static const TrackingUtilities::LookupTable< ROOT::Math::XYVector > & getCosSinLookupTable()
Get the standard lookup table containing equally spaces unit vectors (cos, sin)
static std::vector< float > createCurvBound(YSpan curvSpan, int lastLevel)
Constructs an array with the curvature bounds as generated by the default bin divisions.
static constexpr int getLookupGridLevel()
Returns desired deepness of the trigonometrical lookup table. Used as template parameter for the Trig...
std::function< double(double)> PrecisionFunction
Function type which is used for resolution calculations (resolution=f(curvature)) Takes a curvature v...
std::vector< AItem * > & getItems()
Get items from node.
AY getYBinWidth(int iBin)
Getter for the width of the iBin bin in "r" direction.
AX getXMax() const
Get maximal "Theta" value of the node.
AY getYMax() const
Get maximal "r" value of the node.
AY getYMin() const
Get minimal "r" value of the node.
int getLevel() const
Returns level of the node in tree (i.e., how much ancestors the node has)
AX getXLowerBound(int iBin) const
Get lower "Theta" value of given bin.
AY getYUpperBound(int iBin) const
Get upper "r" value of given bin.
AY getYLowerBound(int iBin) const
Get lower "r" value of given bin.
AX getXMin() const
Get minimal "Theta" value of the node.
AX getXUpperBound(int iBin) const
Get upper "Theta" value of given bin.
QuadTreeProcessor(int lastLevel, int seedLevel, const XYSpans &xySpans, bool debugOutput=false)
Class representing a hit wire in the central drift chamber.
Definition CDCWireHit.h:56
Class which holds precomputed values of a function.
Definition LookupTable.h:50
int getNPoints() const
Return the number of finite sampling points in this lookup table.
bool sameSign(double expected, double actual)
Predicate checking that two values have the same sign.
Abstract base class for different kinds of events.