Belle II Software  release-06-02-00
YScanner.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 <top/reconstruction_cpp/YScanner.h>
10 #include <top/reconstruction_cpp/func.h>
11 #include <top/geometry/TOPGeometryPar.h>
12 #include <framework/logging/Logger.h>
13 #include <cmath>
14 #include <map>
15 
16 namespace Belle2 {
21  namespace TOP {
22 
24 
26  const InverseRaytracer::Solution& sol_dx,
27  const InverseRaytracer::Solution& sol_de,
28  const InverseRaytracer::Solution& sol_dL)
29  {
30  if (sol_dx.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (dx) is zero");
31  if (sol_de.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (de) is zero");
32  if (sol_dL.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (dL) is zero");
33 
34  dLen_dx = dLen_d(sol, sol_dx);
35  dLen_de = dLen_d(sol, sol_de);
36  dLen_dL = dLen_d(sol, sol_dL);
37 
38  dyD_dx = dyD_d(sol, sol_dx);
39  dyD_de = dyD_d(sol, sol_de);
40  dyD_dL = dyD_d(sol, sol_dL);
41 
42  dyB_dx = dyB_d(sol, sol_dx);
43  dyB_de = dyB_d(sol, sol_de);
44  dyB_dL = dyB_d(sol, sol_dL);
45 
46  dFic_dx = dFic_d(sol, sol_dx);
47  dFic_de = dFic_d(sol, sol_de);
48  dFic_dL = dFic_d(sol, sol_dL);
49  }
50 
51 
52  YScanner::YScanner(int moduleID, unsigned N): RaytracerBase(moduleID, c_Unified, c_SemiLinear),
54  m_pixelMasks(PixelMasks(moduleID)),
56  {
57  if (N < 2) {
58  B2FATAL("TOP::YScanner: N must be > 1");
59  return;
60  }
61 
62  // set the table of nominal photon detection efficiencies (incl. wavelength filter)
63 
64  const auto* topgp = TOPGeometryPar::Instance();
65  const auto* geo = topgp->getGeometry();
66  auto qe = geo->getNominalQE(); // get a copy
67  qe.applyFilterTransmission(geo->getWavelengthFilter());
68 
69  double minE = TOPGeometryPar::c_hc / qe.getMaxLambda();
70  double maxE = TOPGeometryPar::c_hc / qe.getMinLambda();
71  if (minE >= maxE) {
72  B2FATAL("TOP::YScanner: quantum efficiency found zero for all wavelengths");
73  return;
74  }
75  m_efficiency.set(minE, (maxE - minE) / (N - 1));
76 
77  const auto& tdc = geo->getNominalTDC();
78  for (unsigned i = 0; i < N; i++) {
79  double e = m_efficiency.getX(i);
80  double lambda = TOPGeometryPar::c_hc / e;
81  double effi = qe.getEfficiency(lambda) * tdc.getEfficiency();
82  m_efficiency.entries.push_back(TableEntry(effi, e, e * e));
83  }
84 
85  // set cosine of total reflection angle using photon mean energy for beta = 1
86 
87  double s = 0;
88  double se = 0;
89  double see = 0;
90  for (const auto& entry : m_efficiency.entries) {
91  double e = entry.x;
92  double p = entry.y * (1 - 1 / pow(topgp->getPhaseIndex(e), 2));
93  s += p;
94  se += p * e;
95  see += p * e * e;
96  }
97  if (s == 0) return;
98  m_meanE0 = se / s;
99  m_rmsE0 = sqrt(see / s - m_meanE0 * m_meanE0);
100  m_cosTotal = sqrt(1 - 1 / pow(topgp->getPhaseIndex(m_meanE0), 2));
101  }
102 
103 
104  void YScanner::clear() const
105  {
106  m_momentum = 0;
107  m_beta = 0;
108  m_length = 0;
109  m_numPhotons = 0;
110  m_meanE = 0;
111  m_rmsE = 0;
112  m_sigmaScat = 0;
113  m_aboveThreshold = false;
116  m_results.clear();
117  m_scanDone = false;
118  }
119 
120 
121  void YScanner::prepare(double momentum, double beta, double length) const
122  {
123  clear();
124 
125  m_momentum = momentum;
126  m_beta = beta;
127  m_length = length;
128 
129  // set photon energy distribution, and the mean and r.m.s of photon energy
130 
131  auto area = setEnergyDistribution(beta);
132  if (area == 0) return;
133 
134  // set number of Cerenkov photons per azimuthal angle
135 
136  m_numPhotons = 370 * area / (2 * M_PI);
137 
138  // set photon energy distribution convoluted with multiple scattering
139 
140  const double radLength = 12.3; // quartz radiation length [cm]
141  double thetaScat = 13.6e-3 / beta / momentum * sqrt(length / 2 / radLength); // r.m.s of multiple scattering angle
142 
143  const auto* topgp = TOPGeometryPar::Instance();
144  double n = topgp->getPhaseIndex(m_meanE);
145  if (beta * n < 1) {
146  B2ERROR("TOP::YScanner::prepare: beta * n < 1 ==> must be a bug!");
147  return;
148  }
149  double dndE = topgp->getPhaseIndexDerivative(m_meanE);
150  double dEdTheta = n * sqrt(pow(beta * n, 2) - 1) / dndE;
151  m_sigmaScat = abs(thetaScat * dEdTheta); // r.m.s of multiple scattering angle converted to photon energy
152 
153  double step = m_energyDistribution.step;
154  int ng = lround(3 * m_sigmaScat / step);
155  std::vector<double> gaus;
156  for (int i = 0; i <= ng; i++) {
157  double x = step * i / m_sigmaScat;
158  gaus.push_back(exp(-0.5 * x * x));
159  }
160 
162  int N = m_energyDistribution.entries.size();
163  double sum = 0;
164  for (int k = -ng; k < N + ng; k++) {
165  double s = 0;
166  double se = 0;
167  double see = 0;
168  for (int i = -ng; i <= ng; i++) {
169  double p = gaus[abs(i)] * m_energyDistribution.getY(k - i);
170  double e = m_energyDistribution.getX(k - i);
171  s += p;
172  se += p * e;
173  see += p * e * e;
174  }
175  if (s > 0) {
176  se /= s;
177  see /= s;
178  }
179  m_quasyEnergyDistribution.entries.push_back(TableEntry(s, se, see));
180  sum += s;
181  }
182  for (auto& entry : m_quasyEnergyDistribution.entries) entry.y /= sum;
183 
184  m_aboveThreshold = true;
185  }
186 
187 
188  double YScanner::setEnergyDistribution(double beta) const
189  {
190  const auto* topgp = TOPGeometryPar::Instance();
191 
193 
194  double s = 0;
195  double se = 0;
196  double see = 0;
197  for (const auto& entry : m_efficiency.entries) {
198  double e = entry.x;
199  double p = std::max(entry.y * (1 - 1 / pow(beta * topgp->getPhaseIndex(e), 2)), 0.0);
200  double ee = entry.xsq;
201  m_energyDistribution.entries.push_back(TableEntry(p, e, ee));
202  s += p;
203  se += p * e;
204  see += p * ee;
205  }
206  if (s == 0) return 0;
207 
208  for (auto& entry : m_energyDistribution.entries) entry.y /= s;
209 
210  m_meanE = se / s;
211  m_rmsE = sqrt(see / s - m_meanE * m_meanE);
212 
213  return s * m_energyDistribution.step;
214  }
215 
216 
217  void YScanner::expand(unsigned col, double yB, double dydz, const Derivatives& D, bool doScan) const
218  {
219  m_results.clear();
220 
221  if (D.dyB_de == 0) return;
222 
223  double minE = m_quasyEnergyDistribution.getXmin();
224  double maxE = m_quasyEnergyDistribution.getXmax();
225  double pixDx = m_pixelPositions.get(col + 1).Dx;
226  double dely = (abs(D.dyB_dL) * m_length + abs(D.dyB_dx) * pixDx) / 2;
227  double y1 = yB - dely;
228  double y2 = yB + dely;
229  if (D.dyB_de > 0) {
230  y1 += D.dyB_de * (minE - m_meanE);
231  y2 += D.dyB_de * (maxE - m_meanE);
232  } else {
233  y1 += D.dyB_de * (maxE - m_meanE);
234  y2 += D.dyB_de * (minE - m_meanE);
235  }
236  double B = m_bars.front().B;
237  int j1 = lround(y1 / B);
238  int j2 = lround(y2 / B) + 1;
239 
240  if (doScan and j2 - j1 <= s_maxReflections) {
241  scan(col, yB, dydz, D, j1, j2);
242  m_scanDone = true;
243  } else {
244  merge(col, dydz, j1, j2);
245  m_scanDone = false;
246  }
247  }
248 
249 
250  void YScanner::scan(unsigned col, double yB, double dydz, const Derivatives& D, int j1, int j2) const
251  {
252 
253  std::map<int, EnergyMask*> masks;
254  for (unsigned row = 0; row < m_pixelPositions.getNumPixelRows(); row++) {
255 
256  int pixelID = m_pixelPositions.pixelID(row, col);
257  if (not m_pixelMasks.isActive(pixelID)) continue;
258 
259  const auto& pixel = m_pixelPositions.get(pixelID);
260  std::vector<PixelProjection> projections[2];
261  PixelProjection proj;
262  for (size_t k = 0; k < m_prism.unfoldedWindows.size(); k++) {
263  projectPixel(pixel.yc, pixel.Dy, k, dydz, proj); // for even j
264  if (proj.Dy > 0) projections[0].push_back(proj);
265  projectPixel(pixel.yc, pixel.Dy, k, -dydz, proj); // for odd j
266  proj.yc = -proj.yc;
267  if (proj.Dy > 0) projections[1].push_back(proj);
268  }
269  if (projections[0].empty() and projections[1].empty()) continue;
270 
271  for (unsigned k = 0; k < 2; k++) {
272  std::sort(projections[k].begin(), projections[k].end());
273  for (auto& projection : projections[k]) {
274  int iDy = lround(projection.Dy * 1000);
275  auto& mask = masks[iDy];
276  if (not mask) {
277  double Dy = projection.Dy;
278  double step = m_quasyEnergyDistribution.step;
279  mask = new EnergyMask(D.dyB_de, D.dyB_dL, D.dyB_dx, Dy, m_length, pixel.Dx, step);
280  }
281  projection.mask = mask;
282  }
283  }
284 
285  double Ecp_old = 0;
286  double wid_old = 1000;
287  m_results.push_back(Result(pixelID));
288  for (int j = j1; j < j2; j++) {
289  double ybar = j * m_bars.front().B - yB;
290  for (const auto& projection : projections[abs(j) % 2]) {
291  double Ecp = (ybar + projection.yc) / D.dyB_de + m_meanE;
292  double wid = projection.mask->getFullWidth();
293  if (abs(Ecp - Ecp_old) > (wid + wid_old) / 2 and m_results.back().sum > 0) {
294  m_results.push_back(Result(pixelID));
295  }
296  integrate(projection.mask, Ecp, m_results.back());
297  Ecp_old = Ecp;
298  wid_old = wid;
299  }
300  }
301 
302  if (m_results.back().sum == 0) m_results.pop_back();
303  }
304 
305  for (auto& result : m_results) result.set();
306 
307  for (auto& mask : masks) {
308  if (mask.second) delete mask.second;
309  }
310 
311  }
312 
313 
314  void YScanner::merge(unsigned col, double dydz, int j1, int j2) const
315  {
316  int Neven = func::getNumOfEven(j1, j2);
317  int Nodd = j2 - j1 - Neven;
318 
319  for (unsigned row = 0; row < m_pixelPositions.getNumPixelRows(); row++) {
320 
321  int pixelID = m_pixelPositions.pixelID(row, col);
322  if (not m_pixelMasks.isActive(pixelID)) continue;
323 
324  const auto& pixel = m_pixelPositions.get(pixelID);
325  double Dy0 = 0;
326  double Dy1 = 0;
327  PixelProjection proj;
328  for (size_t k = 0; k < m_prism.unfoldedWindows.size(); k++) {
329  projectPixel(pixel.yc, pixel.Dy, k, dydz, proj); // for even reflections
330  if (proj.Dy > 0) Dy0 += proj.Dy;
331  projectPixel(pixel.yc, pixel.Dy, k, -dydz, proj); // for odd reflections
332  if (proj.Dy > 0) Dy1 += proj.Dy;
333  }
334  if (Dy0 == 0 and Dy1 == 0) continue;
335 
336  double Dy = (Dy0 * Neven + Dy1 * Nodd) / (Neven + Nodd);
337  Result result(pixelID);
338  result.sum = Dy / m_bars.front().B;
339  result.e0 = m_meanE;
340  result.sigsq = m_rmsE * m_rmsE;
341  m_results.push_back(result);
342  }
343  }
344 
345 
346  void YScanner::integrate(const EnergyMask* energyMask, double Ecp, Result& result) const
347  {
348  const auto& mask = energyMask->getMask();
349 
350  if (mask.empty()) {
351  // direct mask calculation
352  for (size_t i = 0; i < m_quasyEnergyDistribution.entries.size(); i++) {
353  double E = m_quasyEnergyDistribution.getX(i);
354  double m = energyMask->getMask(E - Ecp);
355  if (m > 0) {
356  const auto& entry = m_quasyEnergyDistribution.entries[i];
357  double s = entry.y * m;
358  result.sum += s;
359  result.e0 += entry.x * s;
360  result.sigsq += entry.xsq * s;
361  }
362  }
363  } else {
364  // pre-calculated discrete mask w/ linear interpolation
365  int i0 = m_quasyEnergyDistribution.getIndex(Ecp);
366  double fract = -(Ecp - m_quasyEnergyDistribution.getX(i0)) / m_quasyEnergyDistribution.step;
367  if (fract < 0) {
368  i0++;
369  fract += 1;
370  }
371  int N = m_quasyEnergyDistribution.entries.size() - 1;
372  int M = mask.size() - 1;
373  int i1 = std::max(i0 - M, 0);
374  int i2 = std::min(i0 + M - 1, N);
375  for (int i = i1; i <= i2; i++) {
376  const auto& entry = m_quasyEnergyDistribution.entries[i];
377  double m = mask[abs(i - i0)] * (1 - fract) + mask[abs(i - i0 + 1)] * fract;
378  double s = entry.y * m;
379  result.sum += s;
380  result.e0 += entry.x * s;
381  result.sigsq += entry.xsq * s;
382  }
383  }
384  }
385 
386 
387  double YScanner::prismEntranceY(double y, int k, double dydz) const
388  {
389  const auto& win = m_prism.unfoldedWindows[k];
390  double dz = abs(m_prism.zD - m_prism.zFlat);
391  double z1 = y * win.sz + win.z0 + win.nz * dz;
392  double y1 = y * win.sy + win.y0 + win.ny * dz;
393  return y1 + dydz * (m_prism.zR - z1);
394  }
395 
396 
397  void YScanner::projectPixel(double yc, double size, int k, double dydz, PixelProjection& proj) const
398  {
399  double halfSize = (k - m_prism.k0) % 2 == 0 ? size / 2 : -size / 2;
400  double y1 = prismEntranceY(yc - halfSize, k, dydz);
401  double y2 = prismEntranceY(yc + halfSize, k, dydz);
402 
403  double Bh = m_bars.front().B / 2;
404  y1 = std::max(y1, -Bh);
405  y2 = std::min(y2, Bh);
406 
407  proj.yc = (y1 + y2) / 2;
408  proj.Dy = y2 - y1;
409  }
410 
411  } //TOP
413 } //Belle2
A mask for energy masking.
Definition: EnergyMask.h:24
const std::vector< double > & getMask() const
Returns discrete mask (note: only half of the mask is stored)
Definition: EnergyMask.h:68
Pixel relative efficiencies of a single module.
Pixel masks of a single module.
Definition: PixelMasks.h:22
bool isActive(int pixelID) const
Checks if pixel is active.
Definition: PixelMasks.h:85
Pixel positions and dimensions in module local frame.
int pixelID(unsigned row, unsigned col) const
Transforms pixel row and column to pixel ID Note: for convenience pixel row and column numbering star...
unsigned getNumPixelRows() const
Returns the number of pixel rows.
const PixelData & get(int pixelID) const
Returns pixel data for given pixelID.
Base class with geometry data.
Definition: RaytracerBase.h:27
@ c_Unified
single bar with average width and thickness
Definition: RaytracerBase.h:34
@ c_SemiLinear
semi-linear approximation
Definition: RaytracerBase.h:42
Prism m_prism
prism geometry data
std::vector< BarSegment > m_bars
geometry data of bar segments
static TOPGeometryPar * Instance()
Static method to obtain the pointer to its instance.
static const double c_hc
Planck constant times speed of light in [eV*nm].
double m_cosTotal
cosine of total reflection angle
Definition: YScanner.h:463
void prepare(double momentum, double beta, double length) const
Prepare for the PDF expansion in y for a given track mass hypothesis.
Definition: YScanner.cc:121
Table m_energyDistribution
photon energy distribution
Definition: YScanner.h:473
Table m_quasyEnergyDistribution
photon energy distribution convoluted with multiple scattering
Definition: YScanner.h:474
PixelEfficiencies m_pixelEfficiencies
pixel relative efficiencies
Definition: YScanner.h:459
double m_beta
particle beta
Definition: YScanner.h:467
bool m_scanDone
true if scan performed, false if reflections just merged
Definition: YScanner.h:479
void projectPixel(double yc, double size, int k, double dydz, PixelProjection &proj) const
Calculates a projection of a pixel to prism entrance window (going down-stream the photon).
Definition: YScanner.cc:397
Table m_efficiency
nominal photon detection efficiencies (PDE)
Definition: YScanner.h:460
double m_meanE
mean photon energy
Definition: YScanner.h:470
static int s_maxReflections
maximal number of reflections to perform scan
Definition: YScanner.h:481
void expand(unsigned col, double yB, double dydz, const Derivatives &D, bool doScan) const
Performs the PDF expansion in y for a given pixel column using scan or merge methods.
Definition: YScanner.cc:217
double m_rmsE
r.m.s of photon energy
Definition: YScanner.h:471
bool m_aboveThreshold
true if beta is above the Cerenkov threshold
Definition: YScanner.h:475
double m_sigmaScat
r.m.s.
Definition: YScanner.h:472
void merge(unsigned col, double dydz, int j1, int j2) const
Performs expansion by merging all reflections.
Definition: YScanner.cc:314
void scan(unsigned col, double yB, double dydz, const Derivatives &D, int j1, int j2) const
Performs expansion w/ the scan over reflections.
Definition: YScanner.cc:250
double m_meanE0
mean photon energy for beta = 1
Definition: YScanner.h:461
double m_momentum
particle momentum magnitude
Definition: YScanner.h:466
PixelMasks m_pixelMasks
pixel masks
Definition: YScanner.h:458
void integrate(const EnergyMask *energyMask, double Ecp, Result &result) const
Integrates quasy energy distribution multiplied with energy mask.
Definition: YScanner.cc:346
YScanner(int moduleID, unsigned N=64)
Class constructor.
Definition: YScanner.cc:52
double prismEntranceY(double y, int k, double dydz) const
Calculates y at prism entrance from detection position, reflection number and photon slope.
Definition: YScanner.cc:387
double setEnergyDistribution(double beta) const
Sets photon energy distribution and mean photon energy according to nominal PDE and particle beta.
Definition: YScanner.cc:188
double m_length
length of particle trajectory inside quartz
Definition: YScanner.h:468
double m_rmsE0
r.m.s of photon energy for beta = 1
Definition: YScanner.h:462
std::vector< Result > m_results
results of PDF expansion in y
Definition: YScanner.h:478
PixelPositions m_pixelPositions
positions and sizes of pixels
Definition: YScanner.h:457
void clear() const
Clear mutable variables.
Definition: YScanner.cc:104
double m_numPhotons
number of photons per Cerenkov azimuthal angle per track length
Definition: YScanner.h:469
int getNumOfEven(int j1, int j2)
Returns number of even numbers in the range given by arguments.
Definition: func.h:90
Abstract base class for different kinds of events.
Solution of inverse ray-tracing.
double step
step for numerical derivative calculation
std::vector< TOPGeoPrism::UnfoldedWindow > unfoldedWindows
unfolded prism exit windows
double zFlat
z where flat continues to slanted surface
double zR
maximal z, i.e position of prism-bar joint
double zD
detector (photo-cathode) position
int k0
index of true prism in the vector 'unfoldedWindows'
double dyB_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of unfolded y coordinate at prism entrance.
Definition: YScanner.h:502
double dLen_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of propagation length.
Definition: YScanner.h:490
double dyD_dL
unfolded y coordinate at detection over running parameter of particle trajectory
Definition: YScanner.h:44
double dFic_de
Cerenkov azimuthal angle over photon energy.
Definition: YScanner.h:49
double dLen_de
propagation length over photon energy
Definition: YScanner.h:40
Derivatives()
Default constructor.
Definition: YScanner.h:55
double dyD_de
unfolded y coordinate at detection over photon energy
Definition: YScanner.h:43
double dyD_dx
unfolded y coordinate at detection over photon detection coordinate x
Definition: YScanner.h:42
double dFic_dx
Cerenkov azimuthal angle over photon detection coordinate x.
Definition: YScanner.h:48
double dFic_dL
Cerenkov azimuthal angle over running parameter of particle trajectory.
Definition: YScanner.h:50
double dLen_dL
propagation length over running parameter of particle trajectory
Definition: YScanner.h:41
double dyD_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of unfolded y coordinate at detection.
Definition: YScanner.h:496
double dyB_de
unfolded y coordinate at prism entrance over photon energy
Definition: YScanner.h:46
double dLen_dx
propagation length over photon detection coordinate x
Definition: YScanner.h:39
double dyB_dL
unfolded y coordinate at prism entrance over running parameter of particle trajectory
Definition: YScanner.h:47
double dyB_dx
unfolded y coordinate at prism entrance over photon detection coordinate x
Definition: YScanner.h:45
double dFic_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of Cerenkov azimuthal angle.
Definition: YScanner.h:508
Down-stream projection of a pixel to prism entrance window w/ a clip on bar exit thickness.
Definition: YScanner.h:191
double yc
center in y of clipped pixel projection
Definition: YScanner.h:192
double Dy
size in y of clipped pixel projection
Definition: YScanner.h:193
Single PDF peak data.
Definition: YScanner.h:204
double step
step size
Definition: YScanner.h:129
double getY(int i) const
Returns y for a given index.
Definition: YScanner.h:547
int getIndex(double x) const
Returns index.
Definition: YScanner.h:545
std::vector< TableEntry > entries
table entries
Definition: YScanner.h:130
double getXmax() const
Returns x of the last entry.
Definition: YScanner.h:543
double getX(int i) const
Returns x for a given index.
Definition: YScanner.h:539
void clear()
Clear the content entirely.
Definition: YScanner.h:518
double getXmin() const
Returns x of the first entry.
Definition: YScanner.h:541
void set(double X0, double Step)
Sets the first x and the step, and clears the entries.
Definition: YScanner.h:525