Belle II Software development
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
15namespace Belle2 {
20 namespace TOP {
21
23
25 const InverseRaytracer::Solution& sol_dx,
26 const InverseRaytracer::Solution& sol_de,
27 const InverseRaytracer::Solution& sol_dL)
28 {
29 if (sol_dx.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (dx) is zero");
30 if (sol_de.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (de) is zero");
31 if (sol_dL.step == 0) B2ERROR("TOP::YScanner::Derivatives: step (dL) is zero");
32
33 dLen_dx = dLen_d(sol, sol_dx);
34 dLen_de = dLen_d(sol, sol_de);
35 dLen_dL = dLen_d(sol, sol_dL);
36
37 dyB_dx = dyB_d(sol, sol_dx);
38 dyB_de = dyB_d(sol, sol_de);
39 dyB_dL = dyB_d(sol, sol_dL);
40
41 dFic_dx = dFic_d(sol, sol_dx);
42 dFic_de = dFic_d(sol, sol_de);
43 dFic_dL = dFic_d(sol, sol_dL);
44 }
45
46
47 YScanner::YScanner(int moduleID, unsigned N): RaytracerBase(moduleID, c_Unified, c_SemiLinear),
49 m_pixelMasks(PixelMasks(moduleID)),
51 {
52 if (N < 2) {
53 B2FATAL("TOP::YScanner: N must be > 1");
54 return;
55 }
56
57 // set the table of nominal photon detection efficiencies (incl. wavelength filter)
58
59 const auto* topgp = TOPGeometryPar::Instance();
60 const auto* geo = topgp->getGeometry();
61 auto qe = geo->getNominalQE(); // get a copy
62 qe.applyFilterTransmission(geo->getWavelengthFilter());
63
64 double minE = TOPGeometryPar::c_hc / qe.getMaxLambda();
65 double maxE = TOPGeometryPar::c_hc / qe.getMinLambda();
66 if (minE >= maxE) {
67 B2FATAL("TOP::YScanner: quantum efficiency found zero for all wavelengths");
68 return;
69 }
70 m_efficiency.set(minE, (maxE - minE) / (N - 1));
71
72 const auto& tdc = geo->getNominalTDC();
73 for (unsigned i = 0; i < N; i++) {
74 double e = m_efficiency.getX(i);
75 double lambda = TOPGeometryPar::c_hc / e;
76 double effi = qe.getEfficiency(lambda) * tdc.getEfficiency();
77 m_efficiency.entries.push_back(TableEntry(effi, e, e * e));
78 }
79
80 // set cosine of total reflection angle using photon mean energy for beta = 1
81
82 double s = 0;
83 double se = 0;
84 double see = 0;
85 for (const auto& entry : m_efficiency.entries) {
86 double e = entry.x;
87 double p = entry.y * (1 - 1 / pow(topgp->getPhaseIndex(e), 2));
88 s += p;
89 se += p * e;
90 see += p * e * e;
91 }
92 if (s == 0) return;
93 m_meanE0 = se / s;
94 m_rmsE0 = sqrt(see / s - m_meanE0 * m_meanE0);
95 m_cosTotal = sqrt(1 - 1 / pow(topgp->getPhaseIndex(m_meanE0), 2));
96 }
97
98
99 void YScanner::clear() const
100 {
101 m_momentum = 0;
102 m_beta = 0;
103 m_length = 0;
104 m_numPhotons = 0;
105 m_meanE = 0;
106 m_rmsE = 0;
107 m_sigmaScat = 0;
108 m_sigmaAlpha = 0;
112 m_aboveThreshold = false;
113 m_results.clear();
114 m_scanDone = false;
115 }
116
117
118 void YScanner::prepare(double momentum, double beta, double length) const
119 {
120 clear();
121
122 m_momentum = momentum;
123 m_beta = beta;
124 m_length = length;
125
126 // check for Cherenkov threshold, return if below
127
128 const auto* topgp = TOPGeometryPar::Instance();
129 if (beta * topgp->getPhaseIndex(m_meanE0) < 1) return;
130
131 // set photon energy distribution, and the mean and r.m.s of photon energy
132
133 auto area = setEnergyDistribution(beta);
134 if (area == 0) return;
135
136 // set number of Cerenkov photons per azimuthal angle per centimeter
137
138 m_numPhotons = 370 * area / (2 * M_PI);
139
140 // set multiple scattering and surface roughness sigmas in photon energy units
141
142 const double radLength = 12.3; // quartz radiation length [cm]
143 double thetaScat = 13.6e-3 / beta / momentum * sqrt(length / 2 / radLength); // r.m.s of multiple scattering angle
144
145 double n = topgp->getPhaseIndex(m_meanE);
146 if (beta * n < 1) {
147 B2ERROR("TOP::YScanner::prepare: beta * n < 1 ==> must be a bug!");
148 return;
149 }
150 double dndE = topgp->getPhaseIndexDerivative(m_meanE);
151 double dEdTheta = n * sqrt(pow(beta * n, 2) - 1) / dndE;
152 m_sigmaScat = std::abs(thetaScat * dEdTheta); // r.m.s of multiple scattering angle converted to photon energy
153 m_sigmaAlpha = std::abs(m_bars.back().sigmaAlpha * dEdTheta); // surface roughness converted to photon energy
154
155 // set photon energy distribution convoluted with multiple scattering
156
158
159 m_aboveThreshold = true;
160 }
161
162
163 double YScanner::setEnergyDistribution(double beta) const
164 {
165 const auto* topgp = TOPGeometryPar::Instance();
166
168
169 double s = 0;
170 double se = 0;
171 double see = 0;
172 for (const auto& entry : m_efficiency.entries) {
173 double e = entry.x;
174 double p = std::max(entry.y * (1 - 1 / pow(beta * topgp->getPhaseIndex(e), 2)), 0.0);
175 double ee = entry.xsq;
176 m_energyDistribution.entries.push_back(TableEntry(p, e, ee));
177 s += p;
178 se += p * e;
179 see += p * ee;
180 }
181 if (s == 0) return 0;
182
183 for (auto& entry : m_energyDistribution.entries) entry.y /= s;
184
185 m_meanE = se / s;
186 m_rmsE = sqrt(std::max(see / s - m_meanE * m_meanE, 0.0));
187
188 return s * m_energyDistribution.step;
189 }
190
191
193 {
194 if (m_quasyEnergyDistributions.size() > 1000) {
196 B2ERROR("TOP::YScanner:setQuasyEnergyDistribution: unexpectedly large size of the std::map found, map cleared");
197 }
198
199 double step = m_energyDistribution.step;
200 int ng = lround(3 * sigma / step);
201 auto& quasyEnergyDistribution = m_quasyEnergyDistributions[ng];
202
203 if (quasyEnergyDistribution.entries.empty()) {
204 std::vector<double> gaus;
205 for (int i = 0; i <= ng; i++) {
206 double x = step * i / sigma;
207 gaus.push_back(exp(-0.5 * x * x));
208 }
209
210 quasyEnergyDistribution.set(m_energyDistribution.getX(-ng), step);
211 int N = m_energyDistribution.entries.size();
212 double sum = 0;
213 for (int k = -ng; k < N + ng; k++) {
214 double s = 0;
215 double se = 0;
216 double see = 0;
217 for (int i = -ng; i <= ng; i++) {
218 double p = gaus[std::abs(i)] * m_energyDistribution.getY(k - i);
219 double e = m_energyDistribution.getX(k - i);
220 s += p;
221 se += p * e;
222 see += p * e * e;
223 }
224 if (s > 0) {
225 se /= s;
226 see /= s;
227 }
228 quasyEnergyDistribution.entries.push_back(TableEntry(s, se, see));
229 sum += s;
230 }
231 for (auto& entry : quasyEnergyDistribution.entries) entry.y /= sum;
232 }
233
234 m_quasyEnergyDistribution = &quasyEnergyDistribution;
235 }
236
237
238 void YScanner::expand(unsigned col, double yB, double dydz, const Derivatives& D, int Ny, bool doScan) const
239 {
240 m_results.clear();
241
242 if (D.dyB_de == 0) return;
243
244 double sigma = sqrt(pow(m_sigmaScat, 2) + pow(m_sigmaAlpha, 2) * std::abs(Ny));
246
247 double minE = m_quasyEnergyDistribution->getXmin();
248 double maxE = m_quasyEnergyDistribution->getXmax();
249 double pixDx = m_pixelPositions.get(col + 1).Dx;
250 double dely = (std::abs(D.dyB_dL) * m_length + std::abs(D.dyB_dx) * pixDx) / 2;
251 double y1 = yB - dely;
252 double y2 = yB + dely;
253 if (D.dyB_de > 0) {
254 y1 += D.dyB_de * (minE - m_meanE);
255 y2 += D.dyB_de * (maxE - m_meanE);
256 } else {
257 y1 += D.dyB_de * (maxE - m_meanE);
258 y2 += D.dyB_de * (minE - m_meanE);
259 }
260 double B = m_bars.front().B;
261 int j1 = lround(y1 / B);
262 int j2 = lround(y2 / B) + 1;
263
264 if (doScan and j2 - j1 <= s_maxReflections) {
265 scan(col, yB, dydz, D, j1, j2);
266 m_scanDone = true;
267 } else {
268 merge(col, dydz, j1, j2);
269 m_scanDone = false;
270 }
271 }
272
273
274 void YScanner::scan(unsigned col, double yB, double dydz, const Derivatives& D, int j1, int j2) const
275 {
276
277 std::map<int, EnergyMask*> masks;
278 for (unsigned row = 0; row < m_pixelPositions.getNumPixelRows(); row++) {
279
280 int pixelID = m_pixelPositions.pixelID(row, col);
281 if (not m_pixelMasks.isActive(pixelID)) continue;
282
283 const auto& pixel = m_pixelPositions.get(pixelID);
284 std::vector<PixelProjection> projections[2];
285 PixelProjection proj[2];
286 for (size_t k = 0; k < m_prism.unfoldedWindows.size(); k++) {
287 projectPixel(pixel.yc, pixel.Dy, k, dydz, proj);
288 if (proj[0].Dy > 0) projections[0].push_back(proj[0]);
289 proj[1].yc = -proj[1].yc;
290 if (proj[1].Dy > 0) projections[1].push_back(proj[1]);
291 }
292 if (projections[0].empty() and projections[1].empty()) continue;
293
294 for (unsigned k = 0; k < 2; k++) {
295 std::sort(projections[k].begin(), projections[k].end());
296 for (auto& projection : projections[k]) {
297 int iDy = lround(projection.Dy * 1000);
298 auto& mask = masks[iDy];
299 if (not mask) {
300 double Dy = projection.Dy;
301 double step = m_quasyEnergyDistribution->step;
302 mask = new EnergyMask(D.dyB_de, D.dyB_dL, D.dyB_dx, Dy, m_length, pixel.Dx, step);
303 }
304 projection.mask = mask;
305 }
306 }
307
308 double Ecp_old = 0;
309 double wid_old = 1000;
310 m_results.push_back(Result(pixelID));
311 for (int j = j1; j < j2; j++) {
312 double ybar = j * m_bars.front().B - yB;
313 for (const auto& projection : projections[std::abs(j) % 2]) {
314 double Ecp = (ybar + projection.yc) / D.dyB_de + m_meanE;
315 double wid = projection.mask->getFullWidth();
316 if (std::abs(Ecp - Ecp_old) > (wid + wid_old) / 2 and m_results.back().sum > 0) {
317 m_results.push_back(Result(pixelID));
318 }
319 integrate(projection.mask, Ecp, m_results.back());
320 Ecp_old = Ecp;
321 wid_old = wid;
322 }
323 }
324
325 if (m_results.back().sum == 0) m_results.pop_back();
326 }
327
328 for (auto& result : m_results) result.set();
329
330 for (auto& mask : masks) {
331 if (mask.second) delete mask.second;
332 }
333
334 }
335
336
337 void YScanner::merge(unsigned col, double dydz, int j1, int j2) const
338 {
339 int Neven = func::getNumOfEven(j1, j2);
340 int Nodd = j2 - j1 - Neven;
341
342 for (unsigned row = 0; row < m_pixelPositions.getNumPixelRows(); row++) {
343
344 int pixelID = m_pixelPositions.pixelID(row, col);
345 if (not m_pixelMasks.isActive(pixelID)) continue;
346
347 const auto& pixel = m_pixelPositions.get(pixelID);
348 double Dy0 = 0;
349 double Dy1 = 0;
350 PixelProjection proj[2];
351 for (size_t k = 0; k < m_prism.unfoldedWindows.size(); k++) {
352 projectPixel(pixel.yc, pixel.Dy, k, dydz, proj);
353 if (proj[0].Dy > 0) Dy0 += proj[0].Dy;
354 if (proj[1].Dy > 0) Dy1 += proj[1].Dy;
355 }
356 if (Dy0 == 0 and Dy1 == 0) continue;
357
358 double Dy = (Dy0 * Neven + Dy1 * Nodd) / (Neven + Nodd);
359 Result result(pixelID);
360 result.sum = Dy / m_bars.front().B;
361 result.e0 = m_meanE;
362 result.sigsq = m_rmsE * m_rmsE;
363 m_results.push_back(result);
364 }
365 }
366
367
368 void YScanner::integrate(const EnergyMask* energyMask, double Ecp, Result& result) const
369 {
370 const auto& mask = energyMask->getMask();
371
372 if (mask.empty()) {
373 // direct mask calculation
374 for (size_t i = 0; i < m_quasyEnergyDistribution->entries.size(); i++) {
375 double E = m_quasyEnergyDistribution->getX(i);
376 double m = energyMask->getMask(E - Ecp);
377 if (m > 0) {
378 const auto& entry = m_quasyEnergyDistribution->entries[i];
379 double s = entry.y * m;
380 result.sum += s;
381 result.e0 += entry.x * s;
382 result.sigsq += entry.xsq * s;
383 }
384 }
385 } else {
386 // pre-calculated discrete mask w/ linear interpolation
387 int i0 = m_quasyEnergyDistribution->getIndex(Ecp);
388 double fract = -(Ecp - m_quasyEnergyDistribution->getX(i0)) / m_quasyEnergyDistribution->step;
389 if (fract < 0) {
390 i0++;
391 fract += 1;
392 }
393 int N = m_quasyEnergyDistribution->entries.size() - 1;
394 int M = mask.size() - 1;
395 int i1 = std::max(i0 - M, 0);
396 int i2 = std::min(i0 + M - 1, N);
397 for (int i = i1; i <= i2; i++) {
398 const auto& entry = m_quasyEnergyDistribution->entries[i];
399 double m = mask[std::abs(i - i0)] * (1 - fract) + mask[std::abs(i - i0 + 1)] * fract;
400 double s = entry.y * m;
401 result.sum += s;
402 result.e0 += entry.x * s;
403 result.sigsq += entry.xsq * s;
404 }
405 }
406 }
407
408
409 void YScanner::projectPixel(double yc, double size, int k, double dydz, PixelProjection proj[2]) const
410 {
411 double halfSize = (k - m_prism.k0) % 2 == 0 ? size / 2 : -size / 2;
412 const double ypix[2] = {yc - halfSize, yc + halfSize}; // pixel edges in y
413 double yproj[2][2] = {{0}}; // pixel projections to prism entrance window (second index corresponds to pixel edges)
414
415 const auto& win = m_prism.unfoldedWindows[k];
416 double dz = std::abs(m_prism.zD - m_prism.zFlat);
417 double projectedY = win.y0 + win.ny * dz;
418 double projectedZ = win.z0 + win.nz * dz;
419
420 #pragma omp simd
421 for (int i = 0; i < 2; ++i) {
422 /* Formerly YScanner::prismEntranceY. */
423 double z = ypix[i] * win.sz + projectedZ;
424 double y = ypix[i] * win.sy + projectedY;
425 double dy = dydz * (m_prism.zR - z);
426 yproj[0][i] = y + dy; // even reflections
427 yproj[1][i] = y - dy; // odd reflections
428 }
429
430 double Bh = m_bars.front().B / 2;
431 for (int i = 0; i < 2; ++i) {
432 yproj[i][0] = std::max(yproj[i][0], -Bh);
433 yproj[i][1] = std::min(yproj[i][1], Bh);
434 proj[i].yc = (yproj[i][0] + yproj[i][1]) / 2;
435 proj[i].Dy = yproj[i][1] - yproj[i][0];
436 }
437 }
438
439 } //TOP
441} //Belle2
R E
internal precision of FFTW codelets
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:459
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:118
Table m_energyDistribution
photon energy distribution
Definition: YScanner.h:470
PixelEfficiencies m_pixelEfficiencies
pixel relative efficiencies
Definition: YScanner.h:455
double m_beta
particle beta
Definition: YScanner.h:463
bool m_scanDone
true if scan performed, false if reflections just merged
Definition: YScanner.h:478
Table m_efficiency
nominal photon detection efficiencies (PDE)
Definition: YScanner.h:456
double m_meanE
mean photon energy
Definition: YScanner.h:466
static int s_maxReflections
maximal number of reflections to perform scan
Definition: YScanner.h:480
double m_rmsE
r.m.s of photon energy
Definition: YScanner.h:467
bool m_aboveThreshold
true if beta is above the Cerenkov threshold
Definition: YScanner.h:474
double m_sigmaScat
r.m.s.
Definition: YScanner.h:468
void projectPixel(double yc, double size, int k, double dydz, PixelProjection proj[2]) const
Calculates projections of a pixel to prism entrance window (going down-stream the photon).
Definition: YScanner.cc:409
void merge(unsigned col, double dydz, int j1, int j2) const
Performs expansion by merging all reflections.
Definition: YScanner.cc:337
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:274
double m_meanE0
mean photon energy for beta = 1
Definition: YScanner.h:457
double m_momentum
particle momentum magnitude
Definition: YScanner.h:462
Table * m_quasyEnergyDistribution
a pointer to the element in m_quasyEnergyDistributions
Definition: YScanner.h:473
PixelMasks m_pixelMasks
pixel masks
Definition: YScanner.h:454
void setQuasyEnergyDistribution(double sigma) const
Sets photon energy distribution convoluted with a normalized Gaussian.
Definition: YScanner.cc:192
void integrate(const EnergyMask *energyMask, double Ecp, Result &result) const
Integrates quasy energy distribution multiplied with energy mask.
Definition: YScanner.cc:368
void expand(unsigned col, double yB, double dydz, const Derivatives &D, int Ny, bool doScan) const
Performs the PDF expansion in y for a given pixel column using scan or merge methods.
Definition: YScanner.cc:238
YScanner(int moduleID, unsigned N=64)
Class constructor.
Definition: YScanner.cc:47
std::map< int, Table > m_quasyEnergyDistributions
photon energy distributions convoluted with Gaussian of different widths
Definition: YScanner.h:472
double setEnergyDistribution(double beta) const
Sets photon energy distribution and mean photon energy according to nominal PDE and particle beta.
Definition: YScanner.cc:163
double m_length
length of particle trajectory inside quartz
Definition: YScanner.h:464
double m_rmsE0
r.m.s of photon energy for beta = 1
Definition: YScanner.h:458
std::vector< Result > m_results
results of PDF expansion in y
Definition: YScanner.h:477
PixelPositions m_pixelPositions
positions and sizes of pixels
Definition: YScanner.h:453
void clear() const
Clear mutable variables.
Definition: YScanner.cc:99
double m_numPhotons
number of photons per Cerenkov azimuthal angle per track length
Definition: YScanner.h:465
double m_sigmaAlpha
surface roughness parameter in photon energy units
Definition: YScanner.h:469
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
int getNumOfEven(int j1, int j2)
Returns number of even numbers in the range given by arguments.
Definition: func.h:92
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'
static double dyB_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of unfolded y coordinate at prism entrance.
Definition: YScanner.h:495
static double dLen_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of propagation length.
Definition: YScanner.h:489
double dFic_de
Cerenkov azimuthal angle over photon energy.
Definition: YScanner.h:48
double dLen_de
propagation length over photon energy
Definition: YScanner.h:42
Derivatives()
Default constructor.
Definition: YScanner.h:54
double dFic_dx
Cerenkov azimuthal angle over photon detection coordinate x.
Definition: YScanner.h:47
double dFic_dL
Cerenkov azimuthal angle over running parameter of particle trajectory.
Definition: YScanner.h:49
double dLen_dL
propagation length over running parameter of particle trajectory
Definition: YScanner.h:43
double dyB_de
unfolded y coordinate at prism entrance over photon energy
Definition: YScanner.h:45
double dLen_dx
propagation length over photon detection coordinate x
Definition: YScanner.h:41
double dyB_dL
unfolded y coordinate at prism entrance over running parameter of particle trajectory
Definition: YScanner.h:46
double dyB_dx
unfolded y coordinate at prism entrance over photon detection coordinate x
Definition: YScanner.h:44
static double dFic_d(const InverseRaytracer::Solution &sol0, const InverseRaytracer::Solution &sol1)
Calculates the derivative of Cerenkov azimuthal angle.
Definition: YScanner.h:501
Down-stream projection of a pixel to prism entrance window w/ a clip on bar exit thickness.
Definition: YScanner.h:182
Single PDF peak data.
Definition: YScanner.h:195
double step
step size
Definition: YScanner.h:120
double getY(int i) const
Returns y for a given index.
Definition: YScanner.h:540
int getIndex(double x) const
Returns index.
Definition: YScanner.h:538
std::vector< TableEntry > entries
table entries
Definition: YScanner.h:121
double getXmax() const
Returns x of the last entry.
Definition: YScanner.h:536
double getX(int i) const
Returns x for a given index.
Definition: YScanner.h:532
void clear()
Clear the content entirely.
Definition: YScanner.h:511
double getXmin() const
Returns x of the first entry.
Definition: YScanner.h:534
void set(double X0, double Step)
Sets the first x and the step, and clears the entries.
Definition: YScanner.h:518