Project
Loading...
Searching...
No Matches
Detector.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <FairVolume.h>
13
14#include <TVirtualMC.h>
15#include <TVirtualMCStack.h>
16#include <TGeoVolume.h>
17#include <TGeoTube.h>
18#include <TGeoMatrix.h>
19#include <cmath>
20#include <limits>
21
22#include "DetectorsBase/Stack.h"
26
27using o2::itsmft::Hit;
28
29namespace o2
30{
31namespace rich
32{
33namespace // quadrant equation solver
34{
35double quadrantDeltaPhiEquation(double x, int nTilesPhi, double rMin, double totalBoundaryWidth)
36{
37 const double argument = totalBoundaryWidth * TMath::Cos(x / 2.0) / (2.0 * rMin);
38 if (TMath::Abs(argument) >= 1.0) {
39 return std::numeric_limits<double>::quiet_NaN();
40 }
41 const double rhs = 2.0 * TMath::Pi() / static_cast<double>(nTilesPhi) - (8.0 / static_cast<double>(nTilesPhi)) * TMath::ASin(argument);
42 return rhs - x;
43}
44
45double solveQuadrantDeltaPhi(int nTilesPhi, double rMin, double totalBoundaryWidth)
46{
47 double lower = 0.0;
48 double upper = 1.1 * 2.0 * TMath::Pi() / static_cast<double>(nTilesPhi);
49 double fLower = quadrantDeltaPhiEquation(lower, nTilesPhi, rMin, totalBoundaryWidth);
50 double fUpper = quadrantDeltaPhiEquation(upper, nTilesPhi, rMin, totalBoundaryWidth);
51 if (!std::isfinite(fLower) || !std::isfinite(fUpper) || fLower * fUpper > 0.0) {
52 return -1.0;
53 }
54 constexpr double tolerance = 1.0e-12;
55 constexpr int maxIterations = 200;
56 for (int iteration = 0; iteration < maxIterations; iteration++) {
57 const double middle = 0.5 * (lower + upper);
58 const double fMiddle = quadrantDeltaPhiEquation(middle, nTilesPhi, rMin, totalBoundaryWidth);
59 if (!std::isfinite(fMiddle)) {
60 return -1.0;
61 }
62 if (TMath::Abs(fMiddle) < tolerance || 0.5 * (upper - lower) < tolerance) {
63 return middle;
64 }
65 if (fLower * fMiddle < 0.0) {
66 upper = middle;
67 fUpper = fMiddle;
68 } else {
69 lower = middle;
70 fLower = fMiddle;
71 }
72 }
73 return 0.5 * (lower + upper);
74}
75} // namespace
76
78 : o2::base::DetImpl<Detector>("RCH", true),
79 mTrackData(),
80 mHits(o2::utils::createSimVector<o2::itsmft::Hit>())
81{
82}
83
85 : o2::base::DetImpl<Detector>("RCH", true),
86 mTrackData(),
87 mHits(o2::utils::createSimVector<o2::itsmft::Hit>())
88{
89 auto& richPars = RICHBaseParam::Instance();
90 mRings.resize(richPars.nRings);
91 mNTiles = richPars.nTiles;
92 LOGP(info, "Summary of RICH configuration:\n\tNumber of rings: {}\n\tNumber of tiles per ring: {}", mRings.size(), mNTiles);
93}
94
96{
97 if (mHits) {
99 }
100}
101
107
109{
110 auto& richPars = RICHBaseParam::Instance();
111 const double nGasEffective = richPars.nGasEffective;
112 const double nAerogelEffective = richPars.nAerogelEffective;
113
114 int ifield = 2; // ?
115 float fieldm = 10.0; // ?
117
118 float tmaxfdSi = 0.1; // .10000E+01; // Degree
119 float stemaxSi = 0.0075; // .10000E+01; // cm
120 float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
121 float epsilSi = 1.0E-4; // .10000E+01;
122 float stminSi = 0.0; // cm "Default value used"
123
124 float tmaxfdAir = 0.1; // .10000E+01; // Degree
125 float stemaxAir = .10000E+01; // cm
126 float deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
127 float epsilAir = 1.0E-4; // .10000E+01;
128 float stminAir = 0.0; // cm "Default value used"
129
130 float tmaxfdCer = 0.1; // .10000E+01; // Degree
131 float stemaxCer = .10000E+01; // cm
132 float deemaxCer = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
133 float epsilCer = 1.0E-4; // .10000E+01;
134 float stminCer = 0.0; // cm "Default value used"
135
136 float tmaxfdAerogel = 0.1; // .10000E+01; // Degree
137 float stemaxAerogel = .10000E+01; // cm
138 float deemaxAerogel = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
139 float epsilAerogel = 1.0E-4; // .10000E+01;
140 float stminAerogel = 0.0; // cm "Default value used"
141
142 float tmaxfdCO2 = 0.1; // .10000E+01; // Degree
143 float stemaxCO2 = .10000E+01; // cm
144 float deemaxCO2 = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
145 float epsilCO2 = 1.0E-4; // .10000E+01;
146 float stminCO2 = 0.0; // cm "Default value used"
147
148 float tmaxfdFR4 = 0.1; // degree
149 float stemaxFR4 = 0.1; // cm (1 mm is a reasonable choice for PCB)
150 float deemaxFR4 = 0.1;
151 float epsilFR4 = 1.0E-4;
152 float stminFR4 = 0.0;
153
154 float tmaxfdPEEK = 0.1; // degree
155 float stemaxPEEK = 0.1; // cm (1 mm is a reasonable choice for PCB)
156 float deemaxPEEK = 0.1;
157 float epsilPEEK = 1.0E-4;
158 float stminPEEK = 0.0;
159
160 float tmaxfdAl = 0.1; // degree
161 float stemaxAl = 0.1; // cm
162 float deemaxAl = 0.1;
163 float epsilAl = 1.0E-4;
164 float stminAl = 0.0;
165
166 float tmaxfdArgon = 0.1; // .10000E+01; // Degree
167 float stemaxArgon = .10000E+01; // cm
168 float deemaxArgon = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
169 float epsilArgon = 1.0E-4; // .10000E+01;
170 float stminArgon = 0.0; // cm "Default value used"
171
172 float tmaxfdSiO2 = 0.1; // degree
173 float stemaxSiO2 = 0.1; // cm
174 float deemaxSiO2 = 0.1;
175 float epsilSiO2 = 1.0E-4;
176 float stminSiO2 = 0.0;
177
178 float tmaxfdSilicone = 0.1; // degree
179 float stemaxSilicone = 0.1; // cm
180 float deemaxSilicone = 0.1;
181 float epsilSilicone = 1.0E-4;
182 float stminSilicone = 0.0;
183
184 float tmaxfdSiAbsorber = tmaxfdSi;
185 float stemaxSiAbsorber = stemaxSi;
186 float deemaxSiAbsorber = deemaxSi;
187 float epsilSiAbsorber = epsilSi;
188 float stminSiAbsorber = stminSi;
189
190 // ArmaFlex elastomeric insulation
191 float tmaxfdArmaFlex = 0.1;
192 float stemaxArmaFlex = 0.1; // cm
193 float deemaxArmaFlex = 0.1;
194 float epsilArmaFlex = 1.0E-4;
195 float stminArmaFlex = 0.0;
196
197 // ArmaGel aerogel blanket
198 float tmaxfdArmaGel = 0.1;
199 float stemaxArmaGel = 0.1; // cm
200 float deemaxArmaGel = 0.1;
201 float epsilArmaGel = 1.0E-4;
202 float stminArmaGel = 0.0;
203
204 // High-temperature cofired ceramic
205 float tmaxfdHTCC = 0.1;
206 float stemaxHTCC = 0.1; // cm
207 float deemaxHTCC = 0.1;
208 float epsilHTCC = 1.0E-4;
209 float stminHTCC = 0.0;
210
211 // AIR
212 float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
213 float zAir[4] = {6., 7., 8., 18.};
214 float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
215 float dAir = 1.20479E-3;
216
217 // Carbon fiber
218 float aCf[2] = {12.0107, 1.00794};
219 float zCf[2] = {6., 1.};
220
221 // Silica aerogel https://pdg.lbl.gov/2023/AtomicNuclearProperties/HTML/silica_aerogel.html
222 float aAerogel[3] = {15.9990, 28.0855, 1.00794};
223 float zAerogel[3] = {8., 14., 1.};
224 float wAerogel[3] = {0.543192, 0.453451, 0.003357};
225 float dAerogel = 0.200; // g/cm3
226
227 // CO2 https://pdg.lbl.gov/2023/AtomicNuclearProperties/HTML/carbon_dioxide.html
228 float aCO2[2] = {12.0107, 15.9994};
229 float zCO2[2] = {6., 8.};
230 float wCO2[2] = {0.2729, 0.7271};
231 float dCO2 = 1.842E-3; // g/cm3
232
233 // FR4 for PCBs (approximate composition with H, C, O, Si, Br)
234 float aFR4[5] = {1.00794, 12.0107, 15.9994, 28.0855, 79.904};
235 float zFR4[5] = {1., 6., 8., 14., 35.};
236 float wFR4[5] = {0.068, 0.278, 0.405, 0.180, 0.069};
237 float dFR4 = 1.86; // g/cm3
238
239 // PEEK for insulation shielding
240 float aPEEK[3] = {1.00794, 12.0107, 15.9994};
241 float zPEEK[3] = {1., 6., 8.};
242 float wPEEK[3] = {0.041954, 0.791557, 0.166489};
243 float dPEEK = 1.30; // g/cm3
244
245 // Aluminum cooling plate
246 float aAl = 26.9815385;
247 float zAl = 13.;
248 float dAl = 2.70; // g/cm3
249 float radLengthAl = 8.897; // cm
250 float interactionLengthAl = 39.4; // cm
251
252 // Argon
253 float aArgon = 39.948;
254 float zArgon = 18.;
255 float dArgon = 1.782E-3; // g/cm3
256 float radLengthAr = 1.09708E4; // cm
257 float interactionLengthAr = 6.71717E4; // cm
258
259 // Fused silica, SiO2
260 float aSiO2[2] = {28.0855, 15.9994};
261 float zSiO2[2] = {14., 8.};
262 float wSiO2[2] = {0.467435, 0.532565};
263 float dSiO2 = 2.20; // g/cm3
264
265 // Silicone resin approximated as PDMS: (C2 H6 O Si)n
266 float aSilicone[4] = {12.0107, 1.00794, 15.9994, 28.0855};
267 float zSilicone[4] = {6., 1., 8., 14.};
268 float wSilicone[4] = {0.323940, 0.081555, 0.215759, 0.378746};
269 float dSilicone = 1.05; // g/cm3
270
271 // ArmaFlex approximation of an NBR/PVC closed-cell elastomeric foam (C, H, N, Cl)
272 float aArmaFlex[4] = {12.0107, 1.00794, 14.0067, 35.453};
273 float zArmaFlex[4] = {6., 1., 7., 17.};
274 float wArmaFlex[4] = {0.6053, 0.0720, 0.0391, 0.2836};
275 // Manufacturer range is approximately 0.048--0.096 g/cm3.
276 // Use the midpoint as an effective foam density.
277 float dArmaFlex = 0.072; // g/cm3
278
279 // ArmaGel HT approximated as a silica-aerogel blanket (same as aerogel but with ArmaGel nominal blanket density)
280 float aArmaGel[3] = {15.9990, 28.0855, 1.00794};
281 float zArmaGel[3] = {8., 14., 1.};
282 float wArmaGel[3] = {0.543192, 0.453451, 0.003357};
283 float dArmaGel = 0.180; // g/cm3
284
285 // High-temperature cofired ceramic based on aluminium nitride
286 float aHTCC[2] = {26.9815385, 14.0067};
287 float zHTCC[2] = {13., 7.};
288 float wHTCC[2] = {0.658275, 0.341725};
289 float dHTCC = 3.30; // g/cm3
290
291 o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir);
292 o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir);
293
294 o2::base::Detector::Material(3, "SILICON$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03);
295 o2::base::Detector::Medium(3, "SILICON$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi);
296
297 o2::base::Detector::Mixture(2, "AEROGEL$", aAerogel, zAerogel, dAerogel, 3, wAerogel);
298 o2::base::Detector::Medium(2, "AEROGEL$", 2, 0, ifield, fieldm, tmaxfdAerogel, stemaxAerogel, deemaxAerogel, epsilAerogel, stminAerogel);
299
300 o2::base::Detector::Material(4, "ARGON$", aArgon, zArgon, dArgon, radLengthAr, interactionLengthAr);
301 o2::base::Detector::Medium(4, "ARGON$", 4, 0, ifield, fieldm, tmaxfdArgon, stemaxArgon, deemaxArgon, epsilArgon, stminArgon);
302
303 o2::base::Detector::Mixture(5, "CO2$", aCO2, zCO2, dCO2, 2, wCO2);
304 o2::base::Detector::Medium(5, "CO2$", 5, 0, ifield, fieldm, tmaxfdCO2, stemaxCO2, deemaxCO2, epsilCO2, stminCO2);
305
306 o2::base::Detector::Mixture(6, "FR4$", aFR4, zFR4, dFR4, 5, wFR4);
307 o2::base::Detector::Medium(6, "FR4$", 6, 0, ifield, fieldm, tmaxfdFR4, stemaxFR4, deemaxFR4, epsilFR4, stminFR4);
308
309 o2::base::Detector::Mixture(7, "PEEK$", aPEEK, zPEEK, dPEEK, 3, wPEEK);
310 o2::base::Detector::Medium(7, "PEEK$", 7, 0, ifield, fieldm, tmaxfdPEEK, stemaxPEEK, deemaxPEEK, epsilPEEK, stminPEEK);
311
312 o2::base::Detector::Material(8, "ALUMINUM$", aAl, zAl, dAl, radLengthAl, interactionLengthAl);
313 o2::base::Detector::Medium(8, "ALUMINUM$", 8, 0, ifield, fieldm, tmaxfdAl, stemaxAl, deemaxAl, epsilAl, stminAl);
314
315 o2::base::Detector::Mixture(9, "SIO2$", aSiO2, zSiO2, dSiO2, 2, wSiO2);
316 o2::base::Detector::Medium(9, "SIO2$", 9, 0, ifield, fieldm, tmaxfdSiO2, stemaxSiO2, deemaxSiO2, epsilSiO2, stminSiO2);
317
318 o2::base::Detector::Mixture(10, "SILICONE$", aSilicone, zSilicone, dSilicone, 4, wSilicone);
319 o2::base::Detector::Medium(10, "SILICONE$", 10, 0, ifield, fieldm, tmaxfdSilicone, stemaxSilicone, deemaxSilicone, epsilSilicone, stminSilicone);
320
321 o2::base::Detector::Material(11, "SILICON_ABSORBER$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03);
322 o2::base::Detector::Medium(11, "SILICON_ABSORBER$", 11, 0, ifield, fieldm, tmaxfdSiAbsorber, stemaxSiAbsorber, deemaxSiAbsorber, epsilSiAbsorber, stminSiAbsorber);
323
324 o2::base::Detector::Mixture(12, "ARMAFLEX$", aArmaFlex, zArmaFlex, dArmaFlex, 4, wArmaFlex);
325 o2::base::Detector::Medium(12, "ARMAFLEX$", 12, 0, ifield, fieldm, tmaxfdArmaFlex, stemaxArmaFlex, deemaxArmaFlex, epsilArmaFlex, stminArmaFlex);
326
327 o2::base::Detector::Mixture(13, "ARMAGEL$", aArmaGel, zArmaGel, dArmaGel, 3, wArmaGel);
328 o2::base::Detector::Medium(13, "ARMAGEL$", 13, 0, ifield, fieldm, tmaxfdArmaGel, stemaxArmaGel, deemaxArmaGel, epsilArmaGel, stminArmaGel);
329
330 o2::base::Detector::Mixture(14, "HTCC$", aHTCC, zHTCC, dHTCC, 2, wHTCC);
331 o2::base::Detector::Medium(14, "HTCC$", 14, 0, ifield, fieldm, tmaxfdHTCC, stemaxHTCC, deemaxHTCC, epsilHTCC, stminHTCC);
332
333 // Optical properties
334 auto* mc = TVirtualMC::GetMC();
335 if (!mc) {
336 LOGP(fatal,
337 "RICH: TVirtualMC instance is not available while "
338 "defining optical properties");
339 }
340
341 constexpr double eVInGeV = 1.0e-9;
342
343 auto globalMediumID = [&](int localMediumID, const char* mediumName) {
344 const int id = getMediumID(localMediumID);
345 if (id < 0) {
346 LOGP(fatal, "RICH: no global medium ID found for {} local medium {}", mediumName, localMediumID);
347 }
348 return id;
349 };
350
352 constexpr int nAerogelRindex = 20;
353 double aerogelRindexEnergyGeV[nAerogelRindex] = {1.00 * eVInGeV, 1.06 * eVInGeV, 1.12 * eVInGeV, 1.18 * eVInGeV, 1.23984 * eVInGeV, 1.3051 * eVInGeV, 1.3776 * eVInGeV, 1.45864 * eVInGeV, 1.5498 * eVInGeV, 1.65312 * eVInGeV, 1.7712 * eVInGeV, 1.90745 * eVInGeV, 2.0664 * eVInGeV, 2.25426 * eVInGeV, 2.47968 * eVInGeV, 2.7552 * eVInGeV, 3.0996 * eVInGeV, 3.54241 * eVInGeV, 4.13281 * eVInGeV, 4.5 * eVInGeV};
354 double aerogelRindex[nAerogelRindex] = {1.030402, 1.030410, 1.030418, 1.030426, 1.03044, 1.03045, 1.03046, 1.03047, 1.03049, 1.03051, 1.03054, 1.03057, 1.03061, 1.03066, 1.03073, 1.03082, 1.03095, 1.03114, 1.03144, 1.031695};
355 const double scale = nAerogelEffective / 1.03095; // <-- Original table has n(400 nm = 3.0996 eV) = 1.03095.
356 for (int i = 0; i < nAerogelRindex; ++i) {
357 aerogelRindex[i] *= scale;
358 }
359 // SetCerenkov() requires absorption and efficiency arrays on the same grid as n
360 double aerogelAbsorptionLengthCm = 1.0e5f; // 1 km
361 double aerogelAbsorptionOnRindexGrid[nAerogelRindex];
362 double aerogelDetectionEfficiency[nAerogelRindex] = {};
363 for (int i = 0; i < nAerogelRindex; ++i) {
364 aerogelAbsorptionOnRindexGrid[i] = aerogelAbsorptionLengthCm;
365 }
366 mc->SetCerenkov(globalMediumID(2, "AEROGEL"), nAerogelRindex, aerogelRindexEnergyGeV, aerogelAbsorptionOnRindexGrid, aerogelDetectionEfficiency, aerogelRindex);
367 //
368 // constexpr int nAerogelAbsorption = 2;
369 // double aerogelAbsorptionEnergyGeV[nAerogelAbsorption] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
370 // double aerogelAbsorptionLengthCm[nAerogelAbsorption] = {aerogelAbsorptionLengthCm, aerogelAbsorptionLengthCm};
371 // mc->SetMaterialProperty(globalMediumID(2, "AEROGEL"), "ABSLENGTH", nAerogelAbsorption, aerogelAbsorptionEnergyGeV, aerogelAbsorptionLengthCm);
372 //
373 constexpr int nAerogelRayleigh = 22;
374 double aerogelRayleighEnergyGeV[nAerogelRayleigh] = {1.00 * eVInGeV, 1.06 * eVInGeV, 1.12 * eVInGeV, 1.18 * eVInGeV, 1.23984 * eVInGeV, 1.3051 * eVInGeV, 1.3776 * eVInGeV, 1.45864 * eVInGeV, 1.5498 * eVInGeV, 1.65312 * eVInGeV, 1.7712 * eVInGeV, 1.90745 * eVInGeV, 2.0664 * eVInGeV, 2.25426 * eVInGeV, 2.47968 * eVInGeV, 2.7552 * eVInGeV, 3.0996 * eVInGeV, 3.54241 * eVInGeV, 4.13281 * eVInGeV, 4.95937 * eVInGeV, 6.19921 * eVInGeV, 8.26561 * eVInGeV};
375 double aerogelRayleighLengthCm[nAerogelRayleigh] = {543.253684, 430.307801, 345.247537, 280.204207, 229.885, 187.243, 150.828, 120.001, 94.1609, 72.7371, 55.1954, 41.0359, 29.7931, 21.0359, 14.3678, 9.42672, 5.88506, 3.44971, 1.86207, 0.897989, 0.367816, 0.116379};
376 mc->SetMaterialProperty(globalMediumID(2, "AEROGEL"), "RAYLEIGH", nAerogelRayleigh, aerogelRayleighEnergyGeV, aerogelRayleighLengthCm);
377
379 constexpr int nCO2Optical = 2;
380 double co2EnergyGeV[nCO2Optical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
381 double co2Rindex[nCO2Optical] = {nGasEffective, nGasEffective}; // <- Target gas index for dielectrons
382 double co2AbsorptionLengthCm[nCO2Optical] = {1.0e5, 1.0e5};
383 double co2DetectionEfficiency[nCO2Optical] = {0.0, 0.0};
384 mc->SetCerenkov(globalMediumID(5, "CO2"), nCO2Optical, co2EnergyGeV, co2AbsorptionLengthCm, co2DetectionEfficiency, co2Rindex);
385
387 constexpr int nSiO2Optical = 2;
388 double sio2EnergyGeV[nSiO2Optical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
389 double sio2Rindex[nSiO2Optical] = {1.47, 1.47};
390 double sio2AbsorptionLengthCm[nSiO2Optical] = {1.0e5, 1.0e5};
391 double sio2DetectionEfficiency[nSiO2Optical] = {0.0, 0.0};
392 mc->SetCerenkov(globalMediumID(9, "SIO2"), nSiO2Optical, sio2EnergyGeV, sio2AbsorptionLengthCm, sio2DetectionEfficiency, sio2Rindex);
393
395 constexpr int nSiliconeOptical = 2;
396 double siliconeEnergyGeV[nSiliconeOptical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
397 double siliconeRindex[nSiliconeOptical] = {1.41, 1.41};
398 double siliconeAbsorptionLengthCm[nSiliconeOptical] = {1.0e5, 1.0e5};
399 double siliconeDetectionEfficiency[nSiliconeOptical] = {0.0, 0.0};
400 mc->SetCerenkov(globalMediumID(10, "SILICONE"), nSiliconeOptical, siliconeEnergyGeV, siliconeAbsorptionLengthCm, siliconeDetectionEfficiency, siliconeRindex);
401
403 constexpr int nSiliconOptical = 2;
404 double siliconEnergyGeV[nSiliconOptical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
405 double siliconRindex[nSiliconOptical] = {1.41, 1.41};
406 double siliconAbsorptionLengthCm[nSiliconOptical] = {1.0e5, 1.0e5};
407 double siliconDetectionEfficiency[nSiliconOptical] = {0.0, 0.0};
408 mc->SetCerenkov(globalMediumID(3, "SILICON"), nSiliconOptical, siliconEnergyGeV, siliconAbsorptionLengthCm, siliconDetectionEfficiency, siliconRindex);
409
410 // Si: outer layer just for photon absorption
411 constexpr int nSiliconAbsorberOptical = 2;
412 double siliconAbsorberEnergyGeV[nSiliconAbsorberOptical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
413 double siliconAbsorberAbsorptionLengthCm[nSiliconAbsorberOptical] = {1.0e-7, 1.0e-7}; // 1 nm
414 mc->SetMaterialProperty(globalMediumID(11, "SILICON_ABSORBER"), "ABSLENGTH", nSiliconAbsorberOptical, siliconAbsorberEnergyGeV, siliconAbsorberAbsorptionLengthCm);
415}
416
418{
419 TGeoManager* geoManager = gGeoManager;
420 TGeoVolume* vALIC = geoManager->GetVolume("barrel");
421 if (!vALIC) {
422 LOGP(fatal, "Could not find barrel volume while constructing RICH geometry");
423 }
424 new TGeoVolumeAssembly(GeometryTGeo::getRICHVolPattern());
425 TGeoVolume* vRICH = geoManager->GetVolume(GeometryTGeo::getRICHVolPattern());
426 vALIC->AddNode(vRICH, 2, new TGeoTranslation(0, 30., 0));
427
428 char vstrng[100] = "RICHV";
429 vRICH->SetTitle(vstrng);
430 auto& richPars = RICHBaseParam::Instance();
431
432 // Quadrant parameters
433 const bool flagUseQuadrants = richPars.flagUseQuadrants;
434 const double vesselPhiGap = richPars.vesselPhiGap;
435 const double vesselThicknessShieldingLateral = richPars.vesselThicknessShieldingLateral;
436
437 // shielding parameters
438 double shieldRMin = richPars.shieldRMin;
439 double shieldRMax = richPars.shieldRMax;
440 double innerWallThickness = richPars.innerWallThickness;
441 double outerWallThickness = richPars.outerWallThickness;
442 double shieldLengthZ = richPars.shieldLengthZ;
443 double endCapThicknessZ = richPars.endCapThicknessZ;
444
445 if (innerWallThickness <= 0.0 || outerWallThickness <= 0.0 || endCapThicknessZ <= 0.0 || shieldLengthZ <= 0.0) {
446 LOGP(fatal, "RICH shielding dimensions must be positive");
447 }
448
449 if (shieldRMin + innerWallThickness >= shieldRMax - outerWallThickness) {
450 LOGP(fatal,
451 "RICH shielding walls overlap: inner outer radius = {}, outer inner radius = {}",
452 shieldRMin + innerWallThickness,
453 shieldRMax - outerWallThickness);
454 }
455
456 if (flagUseQuadrants) {
457 if (richPars.nTiles <= 0 || richPars.nTiles % 4 != 0) {
458 LOGP(fatal, "RICH quadrant geometry requires nTiles to be positive and divisible by four; received {}", richPars.nTiles);
459 }
460 if (vesselPhiGap < 0.0 || vesselThicknessShieldingLateral <= 0.0) {
461 LOGP(fatal, "RICH quadrant gap must be non-negative and lateral shielding thickness must be positive");
462 }
463 const double totalBoundaryWidth = 2.0 * vesselThicknessShieldingLateral + vesselPhiGap;
464 if (totalBoundaryWidth >= 2.0 * richPars.rMin || vesselPhiGap >= 2.0 * shieldRMin) {
465 LOGP(fatal, "RICH quadrant boundary dimensions are incompatible with rMin={} cm and shieldRMin={} cm", richPars.rMin, shieldRMin);
466 }
467 const double quadrantDeltaPhi = solveQuadrantDeltaPhi(richPars.nTiles, richPars.rMin, totalBoundaryWidth);
468 if (!(quadrantDeltaPhi > 0.0)) {
469 LOGP(fatal, "RICH could not solve the quadrant module angular pitch");
470 }
471 }
472
473 // Name of the gas mother volume. This name will also be passed
474 // to each Ring so that the ring components become its daughters.
475 const char* richGasMotherName = "RICH_GAS_MOTHER";
476
477 TGeoMedium* medCO2 = gGeoManager->GetMedium("RCH_CO2$");
478 if (!medCO2) {
479 LOGP(fatal, "RICH: CO2 medium not found");
480 }
481
482 TGeoMedium* medPeek = gGeoManager->GetMedium("RCH_PEEK$");
483 if (!medPeek) {
484 LOGP(fatal, "RICH: PEEK medium not found");
485 }
486
487 TGeoMedium* medArmaFlex = gGeoManager->GetMedium("RCH_ARMAFLEX$");
488 if (!medArmaFlex) {
489 LOGP(fatal, "RICH: ArmaFlex medium not found");
490 }
491
492 TGeoMedium* medArmaGel = gGeoManager->GetMedium("RCH_ARMAGEL$");
493 if (!medArmaGel) {
494 LOGP(fatal, "RICH: ArmaGel medium not found");
495 }
496
497 prepareLayout(); // Preparing the positions of the rings and tiles
498
499 // The gas mother includes the side-wall region and both end caps. ( as vessel )
500 const double gasEnvelopeLengthZ = shieldLengthZ + 2.0 * endCapThicknessZ;
501 auto* gasEnvelopeShape = new TGeoTube(shieldRMin, shieldRMax, gasEnvelopeLengthZ / 2.0);
502 auto* gasEnvelopeVolume = new TGeoVolume(richGasMotherName, gasEnvelopeShape, medCO2);
503
504 gasEnvelopeVolume->SetLineColor(kBlue - 9);
505 gasEnvelopeVolume->SetTransparency(90);
506
507 // The gas envelope is a daughter of the general RICH volume.
508 vRICH->AddNode(gasEnvelopeVolume, 1, new TGeoTranslation(0.0, 0.0, 0.0));
509
510 if (!flagUseQuadrants) {
511 // ============================================================
512 // Inner cylindrical insulating wall
513 //
514 // Radial interval:
515 // shieldRMin --> shieldRMin + innerWallThickness
516 //
517 // Longitudinal interval:
518 // -shieldLengthZ/2 --> +shieldLengthZ/2
519 // ============================================================
520 auto* innerWallShape = new TGeoTube(shieldRMin, shieldRMin + innerWallThickness, shieldLengthZ / 2.0);
521 auto* innerWallVolume = new TGeoVolume("RICH_SHIELD_INNER_WALL", innerWallShape, medArmaGel);
522
523 innerWallVolume->SetLineColor(kOrange - 8); // kGray
524 innerWallVolume->SetTransparency(0); // 80
525 gasEnvelopeVolume->AddNode(innerWallVolume, 1, new TGeoTranslation(0.0, 0.0, 0.0));
526
527 // ============================================================
528 // Outer cylindrical insulating wall
529 //
530 // Radial interval:
531 // shieldRMax - outerWallThickness --> shieldRMax
532 //
533 // Longitudinal interval:
534 // -shieldLengthZ/2 --> +shieldLengthZ/2
535 // ============================================================
536
537 auto* outerWallShape = new TGeoTube(shieldRMax - outerWallThickness, shieldRMax, shieldLengthZ / 2.0);
538 auto* outerWallVolume = new TGeoVolume("RICH_SHIELD_OUTER_WALL", outerWallShape, medArmaGel);
539
540 outerWallVolume->SetLineColor(kOrange - 8); // kGray
541 outerWallVolume->SetTransparency(0); // 80
542 gasEnvelopeVolume->AddNode(outerWallVolume, 1, new TGeoTranslation(0.0, 0.0, 0.0));
543
544 // ============================================================
545 // Insulating end caps
546 //
547 // Each end cap covers:
548 // shieldRMin --> shieldRMax
549 //
550 // Each has full thickness:
551 // endCapThicknessZ
552 // ============================================================
553
554 auto* endCapShape = new TGeoTube(shieldRMin, shieldRMax, endCapThicknessZ / 2.0);
555 auto* endCapPlusVolume = new TGeoVolume("RICH_SHIELD_ENDCAP_PLUS", endCapShape, medArmaGel);
556 auto* endCapMinusVolume = new TGeoVolume("RICH_SHIELD_ENDCAP_MINUS", endCapShape, medArmaGel);
557
558 endCapPlusVolume->SetLineColor(kOrange - 8); // kGray
559 endCapPlusVolume->SetTransparency(0); // 80
560
561 endCapMinusVolume->SetLineColor(kOrange - 8); // kGray
562 endCapMinusVolume->SetTransparency(0); // 80
563
564 const double endCapCenterZ = shieldLengthZ / 2.0 + endCapThicknessZ / 2.0;
565
566 gasEnvelopeVolume->AddNode(endCapPlusVolume, 1, new TGeoTranslation(0.0, 0.0, endCapCenterZ));
567 gasEnvelopeVolume->AddNode(endCapMinusVolume, 1, new TGeoTranslation(0.0, 0.0, -endCapCenterZ));
568 } else {
569 // ============================================================
570 // Four independent insulating vessel quadrants
571 // ============================================================
572 const double totalBoundaryWidth = 2.0 * vesselThicknessShieldingLateral + vesselPhiGap;
573 const double moduleDeltaPhi = solveQuadrantDeltaPhi(richPars.nTiles, richPars.rMin, totalBoundaryWidth);
574 const double moduleExtraPhi = TMath::ASin(totalBoundaryWidth / (2.0 * richPars.rMin));
575 const double vesselGapHalfPhi = TMath::ASin(vesselPhiGap / (2.0 * shieldRMin));
576 const double quadrantSpanPhi = TMath::Pi() / 2.0 - 2.0 * vesselGapHalfPhi;
577 const int modulesPerQuadrant = richPars.nTiles / 4;
578
579 // Remaining angular space between the last module of a quadrant and the following vessel gap.
580 const double endModuleExtraPhi = TMath::Pi() / 2.0 - moduleExtraPhi - static_cast<double>(modulesPerQuadrant) * moduleDeltaPhi;
581 const double lateralStartWallSpanPhi = moduleExtraPhi - vesselGapHalfPhi;
582 const double lateralEndWallSpanPhi = endModuleExtraPhi - vesselGapHalfPhi;
583
584 if (quadrantSpanPhi <= 0.0 || lateralStartWallSpanPhi <= 0.0 || lateralEndWallSpanPhi <= 0.0 || lateralStartWallSpanPhi + lateralEndWallSpanPhi >= quadrantSpanPhi) {
585 LOGP(fatal, "RICH invalid quadrant angular dimensions: vessel span={}, start wall span={}, end wall span={}", quadrantSpanPhi, lateralStartWallSpanPhi, lateralEndWallSpanPhi);
586 }
587
588 const double radToDeg = 180.0 / TMath::Pi();
589 const double quadrantSpanDeg = quadrantSpanPhi * radToDeg;
590 const double lateralStartWallSpanDeg = lateralStartWallSpanPhi * radToDeg;
591 const double lateralEndWallSpanDeg = lateralEndWallSpanPhi * radToDeg;
592 const double vesselGapHalfDeg = vesselGapHalfPhi * radToDeg;
593 const double innerGasRadius = shieldRMin + innerWallThickness;
594 const double outerGasRadius = shieldRMax - outerWallThickness;
595
596 // Inner cylindrical shielding, divided into four sectors.
597 auto* innerWallQuadrantShape = new TGeoTubeSeg("RICH_SHIELD_INNER_WALL_QUADRANT_SHAPE", shieldRMin, innerGasRadius, shieldLengthZ / 2.0, 0.0, quadrantSpanDeg);
598
599 // Outer cylindrical shielding, divided into four sectors.
600 auto* outerWallQuadrantShape = new TGeoTubeSeg("RICH_SHIELD_OUTER_WALL_QUADRANT_SHAPE", outerGasRadius, shieldRMax, shieldLengthZ / 2.0, 0.0, quadrantSpanDeg);
601
602 // End caps divided into four sectors.
603 auto* endCapQuadrantShape = new TGeoTubeSeg("RICH_SHIELD_ENDCAP_QUADRANT_SHAPE", shieldRMin, shieldRMax, endCapThicknessZ / 2.0, 0.0, quadrantSpanDeg);
604
605 // Lateral wall at the beginning of each quadrant.
606 auto* lateralStartWallShape = new TGeoTubeSeg("RICH_SHIELD_LATERAL_START_WALL_SHAPE", innerGasRadius, outerGasRadius, shieldLengthZ / 2.0, 0.0, lateralStartWallSpanDeg);
607
608 // Lateral wall at the end of each quadrant.
609 auto* lateralEndWallShape = new TGeoTubeSeg("RICH_SHIELD_LATERAL_END_WALL_SHAPE", innerGasRadius, outerGasRadius, shieldLengthZ / 2.0, 0.0, lateralEndWallSpanDeg);
610
611 auto* innerWallQuadrantVolume = new TGeoVolume("RICH_SHIELD_INNER_WALL_QUADRANT", innerWallQuadrantShape, medArmaGel);
612 auto* outerWallQuadrantVolume = new TGeoVolume("RICH_SHIELD_OUTER_WALL_QUADRANT", outerWallQuadrantShape, medArmaGel);
613 auto* endCapPlusQuadrantVolume = new TGeoVolume("RICH_SHIELD_ENDCAP_PLUS_QUADRANT", endCapQuadrantShape, medArmaGel);
614 auto* endCapMinusQuadrantVolume = new TGeoVolume("RICH_SHIELD_ENDCAP_MINUS_QUADRANT", endCapQuadrantShape, medArmaGel);
615 auto* lateralStartWallVolume = new TGeoVolume("RICH_SHIELD_LATERAL_START_WALL", lateralStartWallShape, medArmaGel);
616 auto* lateralEndWallVolume = new TGeoVolume("RICH_SHIELD_LATERAL_END_WALL", lateralEndWallShape, medArmaGel);
617
618 innerWallQuadrantVolume->SetLineColor(kOrange - 8); // kGray
619 outerWallQuadrantVolume->SetLineColor(kOrange - 8); // kGray
620 endCapPlusQuadrantVolume->SetLineColor(kOrange - 8); // kGray
621 endCapMinusQuadrantVolume->SetLineColor(kOrange - 8); // kGray
622 lateralStartWallVolume->SetLineColor(kOrange - 8); // kGray
623 lateralEndWallVolume->SetLineColor(kOrange - 8); // kGray
624
625 innerWallQuadrantVolume->SetTransparency(0); // 80
626 outerWallQuadrantVolume->SetTransparency(0); // 80
627 endCapPlusQuadrantVolume->SetTransparency(0); // 80
628 endCapMinusQuadrantVolume->SetTransparency(0); // 80
629 lateralStartWallVolume->SetTransparency(0); // 80
630 lateralEndWallVolume->SetTransparency(0); // 80
631
632 const double endCapCenterZ = shieldLengthZ / 2.0 + endCapThicknessZ / 2.0;
633
634 for (int quadrant = 0; quadrant < 4; quadrant++) {
635
636 const double quadrantStartDeg = -45.0 + static_cast<double>(quadrant) * 90.0 + vesselGapHalfDeg;
637 const double quadrantEndDeg = quadrantStartDeg + quadrantSpanDeg;
638
639 auto makeRotation = [&](const char* prefix, double angleDeg) {
640 auto* rotation = new TGeoRotation(Form("%s_%d", prefix, quadrant));
641 rotation->RotateZ(angleDeg);
642 return rotation;
643 };
644
645 // Inner cylindrical wall sector.
646 gasEnvelopeVolume->AddNode(innerWallQuadrantVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation("RICHInnerQuadrantRotation", quadrantStartDeg)));
647 // Outer cylindrical wall sector.
648 gasEnvelopeVolume->AddNode(outerWallQuadrantVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation("RICHOuterQuadrantRotation", quadrantStartDeg)));
649 // Positive-z end cap sector.
650 gasEnvelopeVolume->AddNode(endCapPlusQuadrantVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, endCapCenterZ, makeRotation("RICHEndCapPlusQuadrantRotation", quadrantStartDeg)));
651 // Negative-z end cap sector.
652 gasEnvelopeVolume->AddNode(endCapMinusQuadrantVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, -endCapCenterZ, makeRotation("RICHEndCapMinusQuadrantRotation", quadrantStartDeg)));
653 // Start-side lateral wall.
654 gasEnvelopeVolume->AddNode(lateralStartWallVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation("RICHLateralStartRotation", quadrantStartDeg)));
655 // End-side lateral wall.
656 gasEnvelopeVolume->AddNode(lateralEndWallVolume, quadrant + 1, new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation("RICHLateralEndRotation", quadrantEndDeg - lateralEndWallSpanDeg)));
657 }
658
659 LOGP(info, "RICH quadrant geometry: module pitch={} deg, module boundary half-gap={} deg, vessel half-gap={} deg", moduleDeltaPhi * radToDeg, moduleExtraPhi * radToDeg, vesselGapHalfDeg);
660 }
661
662 // ============================================================ modules
663 for (int iRing{0}; iRing < richPars.nRings; ++iRing) {
664 if (!richPars.oddGeom && iRing == (richPars.nRings / 2)) {
665 continue;
666 }
667 mRings[iRing] = o2::rich::Ring{iRing,
668 richPars.nTiles,
669 richPars.rMin,
670 richPars.rMax,
671 richPars.radiatorThickness,
672 (double)mVTile1[iRing],
673 (double)mVTile2[iRing],
674 (double)mLAerogelZ[iRing],
675 richPars.detectorThickness,
676 (double)mVMirror1[iRing],
677 (double)mVMirror2[iRing],
678 richPars.zBaseSize,
679 (double)mR0Radiator[iRing],
680 (double)mR0PhotoDet[iRing],
681 (double)mTRplusG[iRing],
682 (double)mThetaBi[iRing],
683 richGasMotherName}; // GeometryTGeo::getRICHVolPattern()
684 }
685
686 if (richPars.enableFWDRich) {
687 mFWDRich.createFWDRich(vRICH);
688 }
689 if (richPars.enableBWDRich) {
690 mBWDRich.createBWDRich(vRICH);
691 }
692}
693
695{
696 LOG(info) << "Initialize RICH O2Detector";
697 mGeometryTGeo = GeometryTGeo::Instance();
698 defineSensitiveVolumes();
699}
700
701void Detector::defineSensitiveVolumes()
702{
703 TGeoManager* geoManager = gGeoManager;
704 TGeoVolume* v;
705
706 TString volumeName;
707 LOGP(info, "Adding RICH Sensitive Volumes");
708
709 // The names of the RICH sensitive volumes have the format: Ring(0...mRings.size()-1)
710 for (auto ring : mRings) {
711 for (int j = 0; j < ring.getNTiles(); j++) {
712 volumeName = Form("%s_%d_%d", GeometryTGeo::getRICHSensorPattern(), ring.getPosId(), j);
713 LOGP(info, "Trying {}", volumeName.Data());
714 v = geoManager->GetVolume(volumeName.Data());
715 if (!v) {
716 LOG(error) << "Geometry does not contain volume " << volumeName.Data();
717 geoManager->GetListOfVolumes()->Print();
718 LOG(fatal) << "Could not find volume " << volumeName.Data() << " in the geometry";
719 }
720 LOGP(info, "Adding RICH Sensitive Volume {}", v->GetName());
721 AddSensitiveVolume(v);
722 }
723 }
724}
725
727
729{
730 // This will create a branch in the output tree called Hit, setting the last
731 // parameter to kFALSE means that this collection will not be written to the file,
732 // it will exist only during the simulation
733
734 if (FairRootManager::Instance()) {
735 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true);
736 }
737}
738
740{
741 if (!o2::utils::ShmManager::Instance().isOperational()) {
742 mHits->clear();
743 }
744}
745
746bool Detector::ProcessHits(FairVolume* vol)
747{
748 // This method is called from the MC stepping
749
750 constexpr int kOpticalPhotonPDG = 50000050;
751 const bool isOpticalPhoton = (fMC->TrackPid() == kOpticalPhotonPDG);
752 const bool isChargedParticle = (TMath::Abs(fMC->TrackCharge()) > 0.0);
753 // Reject neutral particles other than optical photons.
754 if (!isChargedParticle && !isOpticalPhoton) {
755 return false;
756 }
757
758 int lay = vol->getVolumeId();
759 int volID = vol->getMCid();
760
761 // Is it needed to keep a track reference when the outer ITS volume is encountered?
762 auto stack = (o2::data::Stack*)fMC->GetStack();
763
764 // Only the active silicon volumes are registered as sensitive in
765 // defineSensitiveVolumes(). The explicit volume-name check is kept
766 // as a safety guard in case additional sensitive volumes are added.
767 if (isOpticalPhoton) {
768 const char* currentVolumeName = fMC->CurrentVolName();
769 const bool isActiveSiliconVolume = currentVolumeName && TString(currentVolumeName).BeginsWith(GeometryTGeo::getRICHSensorPattern());
770 // Create only one hit when entering the active silicon.
771 if (!isActiveSiliconVolume || !fMC->IsTrackEntering()) {
772 return false;
773 }
774 TLorentzVector photonPosition;
775 TLorentzVector photonMomentum;
776 fMC->TrackPosition(photonPosition);
777 fMC->TrackMomentum(photonMomentum);
778 constexpr unsigned char photonStatus = Hit::kTrackEntering;
779 addHit(
780 stack->GetCurrentTrackNumber(),
781 lay,
782 photonPosition.Vect(),
783 photonPosition.Vect(),
784 photonMomentum.Vect(),
785 photonMomentum.E(),
786 photonPosition.T(),
787 0.0,
788 photonStatus,
789 photonStatus);
790
791 stack->addHit(GetDetId());
792
793 return true;
794 }
795
796 if (fMC->IsTrackExiting() && (lay == 0 || lay == mRings.size() - 1)) {
797 // Keep the track refs for the innermost and outermost rings only
798 o2::TrackReference tr(*fMC, GetDetId());
799 tr.setTrackID(stack->GetCurrentTrackNumber());
800 tr.setUserId(lay);
801 stack->addTrackReference(tr);
802 }
803 bool startHit = false, stopHit = false;
804 unsigned char status = 0;
805 if (fMC->IsTrackEntering()) {
806 status |= Hit::kTrackEntering;
807 }
808 if (fMC->IsTrackInside()) {
809 status |= Hit::kTrackInside;
810 }
811 if (fMC->IsTrackExiting()) {
812 status |= Hit::kTrackExiting;
813 }
814 if (fMC->IsTrackOut()) {
815 status |= Hit::kTrackOut;
816 }
817 if (fMC->IsTrackStop()) {
818 status |= Hit::kTrackStopped;
819 }
820 if (fMC->IsTrackAlive()) {
821 status |= Hit::kTrackAlive;
822 }
823
824 // track is entering or created in the volume
825 if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) {
826 startHit = true;
827 } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) {
828 stopHit = true;
829 }
830
831 // increment energy loss at all steps except entrance
832 if (!startHit) {
833 mTrackData.mEnergyLoss += fMC->Edep();
834 }
835 if (!(startHit | stopHit)) {
836 return false; // do noting
837 }
838
839 if (startHit) {
840 mTrackData.mEnergyLoss = 0.;
841 fMC->TrackMomentum(mTrackData.mMomentumStart);
842 fMC->TrackPosition(mTrackData.mPositionStart);
843 mTrackData.mTrkStatusStart = status;
844 mTrackData.mHitStarted = true;
845 }
846 if (stopHit) {
847 TLorentzVector positionStop;
848 fMC->TrackPosition(positionStop);
849 // Retrieve the indices with the volume path
850 int stave(0), halfstave(0), chipinmodule(0), module;
851 fMC->CurrentVolOffID(1, chipinmodule);
852 fMC->CurrentVolOffID(2, module);
853 fMC->CurrentVolOffID(3, halfstave);
854 fMC->CurrentVolOffID(4, stave);
855
856 Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
857 mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
858 mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
859 // p->SetTotalEnergy(vmc->Etot());
860
861 // RS: not sure this is needed
862 // Increment number of Detector det points in TParticle
863 stack->addHit(GetDetId());
864 }
865
866 return true;
867}
868
869o2::itsmft::Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos,
870 const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus,
871 unsigned char endStatus)
872{
873 mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus);
874 return &(mHits->back());
875}
876
878{ // Mere translation of Nicola's code
879 auto& richPars = RICHBaseParam::Instance();
880 LOGP(info, "Setting up {} layout for bRICH", richPars.oddGeom ? "odd" : "even");
881
882 bool isOdd = richPars.oddGeom;
883 mThetaBi.resize(richPars.nRings);
884 mR0Tilt.resize(richPars.nRings);
885 mZ0Tilt.resize(richPars.nRings);
886 mLAerogelZ.resize(richPars.nRings);
887 mTRplusG.resize(richPars.nRings);
888 mMinRadialMirror.resize(richPars.nRings);
889 mMaxRadialMirror.resize(richPars.nRings);
890 mMaxRadialRadiator.resize(richPars.nRings);
891 mVMirror1.resize(richPars.nRings);
892 mVMirror2.resize(richPars.nRings);
893 mVTile1.resize(richPars.nRings);
894 mVTile2.resize(richPars.nRings);
895 mR0Radiator.resize(richPars.nRings);
896 mR0PhotoDet.resize(richPars.nRings);
897
898 // Start from middle one
899 double mVal = TMath::Tan(0.0);
900 mThetaBi[richPars.nRings / 2] = TMath::ATan(mVal);
901 mR0Tilt[richPars.nRings / 2] = richPars.rMax;
902 mZ0Tilt[richPars.nRings / 2] = mR0Tilt[richPars.nRings / 2] * TMath::Tan(mThetaBi[richPars.nRings / 2]);
903 mLAerogelZ[richPars.nRings / 2] = isOdd ? TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMin * richPars.zBaseSize / (TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMax - mVal * richPars.zBaseSize) : 0.f;
904 mTRplusG[richPars.nRings / 2] = richPars.rMax - richPars.rMin;
905 double t = isOdd ? TMath::Tan(TMath::ATan(mVal) + TMath::ATan(richPars.zBaseSize / (2.0 * richPars.rMax * TMath::Sqrt(1.0 + mVal * mVal) - richPars.zBaseSize * mVal))) : 0.f;
906 mMinRadialMirror[richPars.nRings / 2] = richPars.rMax;
907 mMaxRadialRadiator[richPars.nRings / 2] = richPars.rMin;
908
909 // Configure rest of the rings
910 for (int iRing{richPars.nRings / 2 + 1}; iRing < richPars.nRings; ++iRing) {
911 double parA = t;
912 double parB = 2.0 * richPars.rMax / richPars.zBaseSize;
913 mVal = (TMath::Sqrt(parA * parA * parB * parB + parB * parB - 1.0) + parA * parB * parB) / (parB * parB - 1.0);
914 t = TMath::Tan(TMath::ATan(mVal) + TMath::ATan(richPars.zBaseSize / (2.0 * richPars.rMax * TMath::Sqrt(1.0 + mVal * mVal) - richPars.zBaseSize * mVal)));
915 // forward rings
916 mThetaBi[iRing] = TMath::ATan(mVal);
917 mR0Tilt[iRing] = richPars.rMax - richPars.zBaseSize / 2.0 * sin(TMath::ATan(mVal));
918 mZ0Tilt[iRing] = mR0Tilt[iRing] * TMath::Tan(mThetaBi[iRing]);
919 mLAerogelZ[iRing] = TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMin * richPars.zBaseSize / (TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMax - mVal * richPars.zBaseSize);
920 mTRplusG[iRing] = TMath::Sqrt(1.0 + mVal * mVal) * (richPars.rMax - richPars.rMin) - mVal / 2.0 * (richPars.zBaseSize + mLAerogelZ[iRing]);
921 mMinRadialMirror[iRing] = mR0Tilt[iRing] - richPars.zBaseSize / 2.0 * sin(TMath::ATan(mVal));
922 mMaxRadialRadiator[iRing] = richPars.rMin + 2.0 * mLAerogelZ[iRing] / 2.0 * sin(TMath::ATan(mVal));
923 // backward rings
924 mThetaBi[2 * (richPars.nRings / 2) - iRing] = -TMath::ATan(mVal);
925 mR0Tilt[2 * (richPars.nRings / 2) - iRing] = richPars.rMax - richPars.zBaseSize / 2.0 * sin(TMath::ATan(mVal));
926 mZ0Tilt[2 * (richPars.nRings / 2) - iRing] = -mR0Tilt[iRing] * TMath::Tan(mThetaBi[iRing]);
927 mLAerogelZ[2 * (richPars.nRings / 2) - iRing] = TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMin * richPars.zBaseSize / (TMath::Sqrt(1.0 + mVal * mVal) * richPars.rMax - mVal * richPars.zBaseSize);
928 mTRplusG[2 * (richPars.nRings / 2) - iRing] = TMath::Sqrt(1.0 + mVal * mVal) * (richPars.rMax - richPars.rMin) - mVal / 2.0 * (richPars.zBaseSize + mLAerogelZ[iRing]);
929 mMinRadialMirror[2 * (richPars.nRings / 2) - iRing] = mR0Tilt[iRing] - richPars.zBaseSize / 2.0 * sin(TMath::ATan(mVal));
930 mMaxRadialRadiator[2 * (richPars.nRings / 2) - iRing] = richPars.rMin + 2.0 * mLAerogelZ[iRing] / 2.0 * sin(TMath::ATan(mVal));
931 }
932
933 // Dimensioning tiles
934 if (!richPars.flagUseQuadrants) {
935 double percentage = 0.999;
936 for (int iRing = 0; iRing < richPars.nRings; iRing++) {
937 if (iRing == richPars.nRings / 2) {
938 mVMirror1[iRing] = percentage * 2.0 * richPars.rMax * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
939 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
940 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
941 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
942 } else if (iRing > richPars.nRings / 2) {
943 mVMirror1[iRing] = percentage * 2.0 * richPars.rMax * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
944 mVMirror2[iRing] = percentage * 2.0 * mMinRadialMirror[iRing] * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
945 mVTile1[iRing] = percentage * 2.0 * mMaxRadialRadiator[iRing] * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
946 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
947 } else {
948 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
949 mVMirror1[iRing] = percentage * 2.0 * mMinRadialMirror[iRing] * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
950 mVTile2[iRing] = percentage * 2.0 * mMaxRadialRadiator[iRing] * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
951 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / double(richPars.nTiles));
952 }
953 }
954
955 } else {
956
957 const double totalBoundaryWidth = 2.0 * richPars.vesselThicknessShieldingLateral + richPars.vesselPhiGap;
958 const double quadrantDeltaPhi = solveQuadrantDeltaPhi(richPars.nTiles, richPars.rMin, totalBoundaryWidth);
959 if (!(quadrantDeltaPhi > 0.0)) {
960 LOGP(fatal, "RICH could not solve the quadrant module angular pitch");
961 }
962 const double halfWidthFactor = TMath::Tan(quadrantDeltaPhi / 2.0);
963 double percentage = 0.999;
964 for (int iRing = 0; iRing < richPars.nRings; iRing++) {
965 if (iRing == richPars.nRings / 2) {
966 mVMirror1[iRing] = percentage * 2.0 * richPars.rMax * halfWidthFactor;
967 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * halfWidthFactor;
968 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
969 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
970 } else if (iRing > richPars.nRings / 2) {
971 mVMirror1[iRing] = percentage * 2.0 * richPars.rMax * halfWidthFactor;
972 mVMirror2[iRing] = percentage * 2.0 * mMinRadialMirror[iRing] * halfWidthFactor;
973 mVTile1[iRing] = percentage * 2.0 * mMaxRadialRadiator[iRing] * halfWidthFactor;
974 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
975
976 } else {
977 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * halfWidthFactor;
978 mVMirror1[iRing] = percentage * 2.0 * mMinRadialMirror[iRing] * halfWidthFactor;
979 mVTile2[iRing] = percentage * 2.0 * mMaxRadialRadiator[iRing] * halfWidthFactor;
980 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
981 }
982 }
983 }
984
985 // ============================================================
986 // Cylindrical aerogel geometry
987 // ============================================================
988 //
989 // In this mode the photosensors remain projective, but all
990 // aerogel tiles:
991 //
992 // - have identical dimensions;
993 // - are parallel to the beam axis;
994 // - lie at the same cylindrical radius;
995 // - are uniformly distributed along Z.
996 //
997 if (richPars.useCylindricalAerogel) {
998
999 // In the even geometry the central projective ring is skipped
1000 // in createGeometry(), so the number of actual aerogel rows is
1001 // nRings - 1.
1002 const int nAerogelRows = richPars.oddGeom ? richPars.nRings : richPars.nRings - 1;
1003
1004 if (nAerogelRows <= 0) {
1005 LOGP(fatal, "Invalid number of cylindrical aerogel rows: {}", nAerogelRows);
1006 }
1007
1008 if (richPars.nTiles <= 0) {
1009 LOGP(fatal, "Invalid number of aerogel tiles in phi: {}", richPars.nTiles);
1010 }
1011
1012 const double thetaRef = 2.0 * TMath::ATan(TMath::Exp(-richPars.cylindricalAerogelEtaRef));
1013 const double cylindricalAerogelTileSizeZ = (2.0 * richPars.rMin / TMath::Tan(thetaRef)) / static_cast<double>(nAerogelRows);
1014 // const double cylindricalAerogelTileSizeRPhi = 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / static_cast<double>(richPars.nTiles));
1015 double cylindricalAerogelTileSizeRPhi = 0.0;
1016
1017 if (!richPars.flagUseQuadrants) {
1018 // Original uniform-phi geometry.
1019 cylindricalAerogelTileSizeRPhi = 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() / static_cast<double>(richPars.nTiles));
1020 } else {
1021 const double totalBoundaryWidth = 2.0 * richPars.vesselThicknessShieldingLateral + richPars.vesselPhiGap;
1022 const double quadrantDeltaPhi = solveQuadrantDeltaPhi(richPars.nTiles, richPars.rMin, totalBoundaryWidth);
1023 cylindricalAerogelTileSizeRPhi = 2.0 * richPars.rMin * TMath::Tan(quadrantDeltaPhi / 2.0);
1024 }
1025
1026 LOGP(info, "Cylindrical aerogel: rows={}, etaRef={}, tileSizeZ={} cm, tileSizeRPhi={} cm", nAerogelRows, richPars.cylindricalAerogelEtaRef, cylindricalAerogelTileSizeZ, cylindricalAerogelTileSizeRPhi);
1027
1028 for (int iRing = 0; iRing < richPars.nRings; iRing++) {
1029 mLAerogelZ[iRing] = cylindricalAerogelTileSizeZ;
1030
1031 // Equal values make the TGeoArb8 a rectangle instead of the projective trapezoid.
1032 mVTile1[iRing] = cylindricalAerogelTileSizeRPhi;
1033 mVTile2[iRing] = cylindricalAerogelTileSizeRPhi;
1034 }
1035 }
1036
1037 // Translation parameters
1038 for (size_t iRing{0}; iRing < richPars.nRings; ++iRing) {
1039
1040 if (richPars.useCylindricalAerogel) {
1041 mR0Radiator[iRing] = richPars.rMin + richPars.radiatorThickness / 2.0;
1042 } else {
1043 // Original projective aerogel position.
1044 mR0Radiator[iRing] = mR0Tilt[iRing] - (mTRplusG[iRing] - richPars.radiatorThickness / 2.0) * TMath::Cos(mThetaBi[iRing]);
1045 }
1046
1047 // Photosensors remain projective for both configurations.
1048 mR0PhotoDet[iRing] = mR0Tilt[iRing] - richPars.detectorThickness / 2.0 * TMath::Cos(mThetaBi[iRing]);
1049 }
1050
1051 // FWD and BWD RICH
1052 if (richPars.enableFWDRich) {
1053 LOGP(info, "Setting up FWD RICH layout");
1055 richPars.rFWDMin,
1056 richPars.rFWDMax,
1057 richPars.zAerogelMin,
1058 richPars.zAerogelMax - richPars.zAerogelMin,
1059 richPars.zArgonMin,
1060 richPars.zArgonMax - richPars.zArgonMin,
1061 richPars.zSiliconMin,
1062 richPars.zSiliconMax - richPars.zSiliconMin);
1063 }
1064 if (richPars.enableBWDRich) {
1065 LOGP(info, "Setting up BWD RICH layout");
1067 richPars.rFWDMin,
1068 richPars.rFWDMax,
1069 richPars.zAerogelMin,
1070 richPars.zAerogelMax - richPars.zAerogelMin,
1071 richPars.zArgonMin,
1072 richPars.zArgonMax - richPars.zArgonMin,
1073 richPars.zSiliconMin,
1074 richPars.zSiliconMax - richPars.zSiliconMin);
1075 }
1076}
1077} // namespace rich
1078} // namespace o2
1079
Definition of the Stack class.
Definition of the ITSMFT Hit class.
int32_t i
uint32_t j
Definition RawData.h:0
uint32_t stack
Definition RawData.h:1
ClassImp(o2::rich::Detector)
void setUserId(Int_t userId)
void setTrackID(Int_t track)
void Mixture(Int_t imat, const char *name, Float_t *a, Float_t *z, Float_t dens, Int_t nlmat, Float_t *wmat)
Definition Detector.cxx:66
void Medium(Int_t numed, const char *name, Int_t nmat, Int_t isvol, Int_t ifield, Float_t fieldm, Float_t tmaxfd, Float_t stemax, Float_t deemax, Float_t epsil, Float_t stmin, Float_t *ubuf=nullptr, Int_t nbuf=0)
Definition Detector.cxx:72
int getMediumID(int imed) const
Definition Detector.h:135
static void initFieldTrackingParams(int &mode, float &maxfield)
Definition Detector.cxx:143
void Material(Int_t imat, const char *name, Float_t a, Float_t z, Float_t dens, Float_t radl, Float_t absl, Float_t *buf=nullptr, Int_t nwbuf=0)
Definition Detector.cxx:59
std::string addNameTo(const char *ext) const
Definition Detector.h:150
void createBWDRich(TGeoVolume *motherVolume)
Definition RICHRing.cxx:653
void InitializeO2Detector() override
Definition Detector.cxx:694
std::vector< double > mZ0Tilt
Definition Detector.h:94
std::vector< double > mLAerogelZ
Definition Detector.h:95
std::vector< double > mR0PhotoDet
Definition Detector.h:105
std::vector< double > mVTile2
Definition Detector.h:103
std::vector< double > mR0Radiator
Definition Detector.h:104
std::vector< double > mThetaBi
Definition Detector.h:92
bool ProcessHits(FairVolume *v=nullptr) override
Definition Detector.cxx:746
void Register() override
Definition Detector.cxx:728
std::vector< double > mR0Tilt
Definition Detector.h:93
std::vector< double > mMaxRadialMirror
Definition Detector.h:98
std::vector< double > mVMirror2
Definition Detector.h:101
std::vector< double > mMaxRadialRadiator
Definition Detector.h:99
std::vector< double > mTRplusG
Definition Detector.h:96
std::vector< double > mMinRadialMirror
Definition Detector.h:97
o2::itsmft::Hit * addHit(int trackID, int detID, const TVector3 &startPos, const TVector3 &endPos, const TVector3 &startMom, double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus)
Definition Detector.cxx:869
std::vector< double > mVTile1
Definition Detector.h:102
void ConstructGeometry() override
Definition Detector.cxx:102
void EndOfEvent() override
Definition Detector.cxx:726
void Reset() override
Definition Detector.cxx:739
std::vector< double > mVMirror1
Definition Detector.h:100
void createFWDRich(TGeoVolume *motherVolume)
Definition RICHRing.cxx:613
static const char * getRICHSensorBWDPattern()
static const char * getRICHSensorPattern()
static GeometryTGeo * Instance()
static const char * getRICHVolPattern()
static const char * getRICHSensorFWDPattern()
static ShmManager & Instance()
Definition ShmManager.h:61
GLint GLenum GLint x
Definition glcorearb.h:403
const GLdouble * v
Definition glcorearb.h:832
GLboolean * data
Definition glcorearb.h:298
GLuint id
Definition glcorearb.h:650
D const SVectorGPU< T, D > & rhs
Definition SMatrixGPU.h:193
void freeSimVector(std::vector< T > *ptr)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Common utility functions.
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"