111 const double nGasEffective = richPars.nGasEffective;
112 const double nAerogelEffective = richPars.nAerogelEffective;
118 float tmaxfdSi = 0.1;
119 float stemaxSi = 0.0075;
120 float deemaxSi = 0.1;
121 float epsilSi = 1.0E-4;
124 float tmaxfdAir = 0.1;
125 float stemaxAir = .10000E+01;
126 float deemaxAir = 0.1;
127 float epsilAir = 1.0E-4;
128 float stminAir = 0.0;
130 float tmaxfdCer = 0.1;
131 float stemaxCer = .10000E+01;
132 float deemaxCer = 0.1;
133 float epsilCer = 1.0E-4;
134 float stminCer = 0.0;
136 float tmaxfdAerogel = 0.1;
137 float stemaxAerogel = .10000E+01;
138 float deemaxAerogel = 0.1;
139 float epsilAerogel = 1.0E-4;
140 float stminAerogel = 0.0;
142 float tmaxfdCO2 = 0.1;
143 float stemaxCO2 = .10000E+01;
144 float deemaxCO2 = 0.1;
145 float epsilCO2 = 1.0E-4;
146 float stminCO2 = 0.0;
148 float tmaxfdFR4 = 0.1;
149 float stemaxFR4 = 0.1;
150 float deemaxFR4 = 0.1;
151 float epsilFR4 = 1.0E-4;
152 float stminFR4 = 0.0;
154 float tmaxfdPEEK = 0.1;
155 float stemaxPEEK = 0.1;
156 float deemaxPEEK = 0.1;
157 float epsilPEEK = 1.0E-4;
158 float stminPEEK = 0.0;
160 float tmaxfdAl = 0.1;
161 float stemaxAl = 0.1;
162 float deemaxAl = 0.1;
163 float epsilAl = 1.0E-4;
166 float tmaxfdArgon = 0.1;
167 float stemaxArgon = .10000E+01;
168 float deemaxArgon = 0.1;
169 float epsilArgon = 1.0E-4;
170 float stminArgon = 0.0;
172 float tmaxfdSiO2 = 0.1;
173 float stemaxSiO2 = 0.1;
174 float deemaxSiO2 = 0.1;
175 float epsilSiO2 = 1.0E-4;
176 float stminSiO2 = 0.0;
178 float tmaxfdSilicone = 0.1;
179 float stemaxSilicone = 0.1;
180 float deemaxSilicone = 0.1;
181 float epsilSilicone = 1.0E-4;
182 float stminSilicone = 0.0;
184 float tmaxfdSiAbsorber = tmaxfdSi;
185 float stemaxSiAbsorber = stemaxSi;
186 float deemaxSiAbsorber = deemaxSi;
187 float epsilSiAbsorber = epsilSi;
188 float stminSiAbsorber = stminSi;
191 float tmaxfdArmaFlex = 0.1;
192 float stemaxArmaFlex = 0.1;
193 float deemaxArmaFlex = 0.1;
194 float epsilArmaFlex = 1.0E-4;
195 float stminArmaFlex = 0.0;
198 float tmaxfdArmaGel = 0.1;
199 float stemaxArmaGel = 0.1;
200 float deemaxArmaGel = 0.1;
201 float epsilArmaGel = 1.0E-4;
202 float stminArmaGel = 0.0;
205 float tmaxfdHTCC = 0.1;
206 float stemaxHTCC = 0.1;
207 float deemaxHTCC = 0.1;
208 float epsilHTCC = 1.0E-4;
209 float stminHTCC = 0.0;
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;
218 float aCf[2] = {12.0107, 1.00794};
219 float zCf[2] = {6., 1.};
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;
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;
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};
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};
246 float aAl = 26.9815385;
249 float radLengthAl = 8.897;
250 float interactionLengthAl = 39.4;
253 float aArgon = 39.948;
255 float dArgon = 1.782E-3;
256 float radLengthAr = 1.09708E4;
257 float interactionLengthAr = 6.71717E4;
260 float aSiO2[2] = {28.0855, 15.9994};
261 float zSiO2[2] = {14., 8.};
262 float wSiO2[2] = {0.467435, 0.532565};
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;
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};
277 float dArmaFlex = 0.072;
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;
286 float aHTCC[2] = {26.9815385, 14.0067};
287 float zHTCC[2] = {13., 7.};
288 float wHTCC[2] = {0.658275, 0.341725};
292 o2::base::Detector::Medium(1,
"AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir);
295 o2::base::Detector::Medium(3,
"SILICON$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi);
298 o2::base::Detector::Medium(2,
"AEROGEL$", 2, 0, ifield, fieldm, tmaxfdAerogel, stemaxAerogel, deemaxAerogel, epsilAerogel, stminAerogel);
301 o2::base::Detector::Medium(4,
"ARGON$", 4, 0, ifield, fieldm, tmaxfdArgon, stemaxArgon, deemaxArgon, epsilArgon, stminArgon);
304 o2::base::Detector::Medium(5,
"CO2$", 5, 0, ifield, fieldm, tmaxfdCO2, stemaxCO2, deemaxCO2, epsilCO2, stminCO2);
307 o2::base::Detector::Medium(6,
"FR4$", 6, 0, ifield, fieldm, tmaxfdFR4, stemaxFR4, deemaxFR4, epsilFR4, stminFR4);
310 o2::base::Detector::Medium(7,
"PEEK$", 7, 0, ifield, fieldm, tmaxfdPEEK, stemaxPEEK, deemaxPEEK, epsilPEEK, stminPEEK);
313 o2::base::Detector::Medium(8,
"ALUMINUM$", 8, 0, ifield, fieldm, tmaxfdAl, stemaxAl, deemaxAl, epsilAl, stminAl);
316 o2::base::Detector::Medium(9,
"SIO2$", 9, 0, ifield, fieldm, tmaxfdSiO2, stemaxSiO2, deemaxSiO2, epsilSiO2, stminSiO2);
319 o2::base::Detector::Medium(10,
"SILICONE$", 10, 0, ifield, fieldm, tmaxfdSilicone, stemaxSilicone, deemaxSilicone, epsilSilicone, stminSilicone);
322 o2::base::Detector::Medium(11,
"SILICON_ABSORBER$", 11, 0, ifield, fieldm, tmaxfdSiAbsorber, stemaxSiAbsorber, deemaxSiAbsorber, epsilSiAbsorber, stminSiAbsorber);
325 o2::base::Detector::Medium(12,
"ARMAFLEX$", 12, 0, ifield, fieldm, tmaxfdArmaFlex, stemaxArmaFlex, deemaxArmaFlex, epsilArmaFlex, stminArmaFlex);
328 o2::base::Detector::Medium(13,
"ARMAGEL$", 13, 0, ifield, fieldm, tmaxfdArmaGel, stemaxArmaGel, deemaxArmaGel, epsilArmaGel, stminArmaGel);
331 o2::base::Detector::Medium(14,
"HTCC$", 14, 0, ifield, fieldm, tmaxfdHTCC, stemaxHTCC, deemaxHTCC, epsilHTCC, stminHTCC);
334 auto* mc = TVirtualMC::GetMC();
337 "RICH: TVirtualMC instance is not available while "
338 "defining optical properties");
341 constexpr double eVInGeV = 1.0e-9;
343 auto globalMediumID = [&](
int localMediumID,
const char* mediumName) {
346 LOGP(fatal,
"RICH: no global medium ID found for {} local medium {}", mediumName, localMediumID);
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;
356 for (
int i = 0;
i < nAerogelRindex; ++
i) {
357 aerogelRindex[
i] *= scale;
360 double aerogelAbsorptionLengthCm = 1.0e5f;
361 double aerogelAbsorptionOnRindexGrid[nAerogelRindex];
362 double aerogelDetectionEfficiency[nAerogelRindex] = {};
363 for (
int i = 0;
i < nAerogelRindex; ++
i) {
364 aerogelAbsorptionOnRindexGrid[
i] = aerogelAbsorptionLengthCm;
366 mc->SetCerenkov(globalMediumID(2,
"AEROGEL"), nAerogelRindex, aerogelRindexEnergyGeV, aerogelAbsorptionOnRindexGrid, aerogelDetectionEfficiency, aerogelRindex);
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);
379 constexpr int nCO2Optical = 2;
380 double co2EnergyGeV[nCO2Optical] = {1.0 * eVInGeV, 8.26561 * eVInGeV};
381 double co2Rindex[nCO2Optical] = {nGasEffective, nGasEffective};
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);
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);
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);
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);
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};
414 mc->SetMaterialProperty(globalMediumID(11,
"SILICON_ABSORBER"),
"ABSLENGTH", nSiliconAbsorberOptical, siliconAbsorberEnergyGeV, siliconAbsorberAbsorptionLengthCm);
419 TGeoManager* geoManager = gGeoManager;
420 TGeoVolume* vALIC = geoManager->GetVolume(
"barrel");
422 LOGP(fatal,
"Could not find barrel volume while constructing RICH geometry");
426 vALIC->AddNode(vRICH, 2,
new TGeoTranslation(0, 30., 0));
428 char vstrng[100] =
"RICHV";
429 vRICH->SetTitle(vstrng);
433 const bool flagUseQuadrants = richPars.flagUseQuadrants;
434 const double vesselPhiGap = richPars.vesselPhiGap;
435 const double vesselThicknessShieldingLateral = richPars.vesselThicknessShieldingLateral;
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;
445 if (innerWallThickness <= 0.0 || outerWallThickness <= 0.0 || endCapThicknessZ <= 0.0 || shieldLengthZ <= 0.0) {
446 LOGP(fatal,
"RICH shielding dimensions must be positive");
449 if (shieldRMin + innerWallThickness >= shieldRMax - outerWallThickness) {
451 "RICH shielding walls overlap: inner outer radius = {}, outer inner radius = {}",
452 shieldRMin + innerWallThickness,
453 shieldRMax - outerWallThickness);
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);
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");
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);
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");
475 const char* richGasMotherName =
"RICH_GAS_MOTHER";
477 TGeoMedium* medCO2 = gGeoManager->GetMedium(
"RCH_CO2$");
479 LOGP(fatal,
"RICH: CO2 medium not found");
482 TGeoMedium* medPeek = gGeoManager->GetMedium(
"RCH_PEEK$");
484 LOGP(fatal,
"RICH: PEEK medium not found");
487 TGeoMedium* medArmaFlex = gGeoManager->GetMedium(
"RCH_ARMAFLEX$");
489 LOGP(fatal,
"RICH: ArmaFlex medium not found");
492 TGeoMedium* medArmaGel = gGeoManager->GetMedium(
"RCH_ARMAGEL$");
494 LOGP(fatal,
"RICH: ArmaGel medium not found");
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);
504 gasEnvelopeVolume->SetLineColor(kBlue - 9);
505 gasEnvelopeVolume->SetTransparency(90);
508 vRICH->AddNode(gasEnvelopeVolume, 1,
new TGeoTranslation(0.0, 0.0, 0.0));
510 if (!flagUseQuadrants) {
520 auto* innerWallShape =
new TGeoTube(shieldRMin, shieldRMin + innerWallThickness, shieldLengthZ / 2.0);
521 auto* innerWallVolume =
new TGeoVolume(
"RICH_SHIELD_INNER_WALL", innerWallShape, medArmaGel);
523 innerWallVolume->SetLineColor(kOrange - 8);
524 innerWallVolume->SetTransparency(0);
525 gasEnvelopeVolume->AddNode(innerWallVolume, 1,
new TGeoTranslation(0.0, 0.0, 0.0));
537 auto* outerWallShape =
new TGeoTube(shieldRMax - outerWallThickness, shieldRMax, shieldLengthZ / 2.0);
538 auto* outerWallVolume =
new TGeoVolume(
"RICH_SHIELD_OUTER_WALL", outerWallShape, medArmaGel);
540 outerWallVolume->SetLineColor(kOrange - 8);
541 outerWallVolume->SetTransparency(0);
542 gasEnvelopeVolume->AddNode(outerWallVolume, 1,
new TGeoTranslation(0.0, 0.0, 0.0));
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);
558 endCapPlusVolume->SetLineColor(kOrange - 8);
559 endCapPlusVolume->SetTransparency(0);
561 endCapMinusVolume->SetLineColor(kOrange - 8);
562 endCapMinusVolume->SetTransparency(0);
564 const double endCapCenterZ = shieldLengthZ / 2.0 + endCapThicknessZ / 2.0;
566 gasEnvelopeVolume->AddNode(endCapPlusVolume, 1,
new TGeoTranslation(0.0, 0.0, endCapCenterZ));
567 gasEnvelopeVolume->AddNode(endCapMinusVolume, 1,
new TGeoTranslation(0.0, 0.0, -endCapCenterZ));
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;
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;
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);
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;
597 auto* innerWallQuadrantShape =
new TGeoTubeSeg(
"RICH_SHIELD_INNER_WALL_QUADRANT_SHAPE", shieldRMin, innerGasRadius, shieldLengthZ / 2.0, 0.0, quadrantSpanDeg);
600 auto* outerWallQuadrantShape =
new TGeoTubeSeg(
"RICH_SHIELD_OUTER_WALL_QUADRANT_SHAPE", outerGasRadius, shieldRMax, shieldLengthZ / 2.0, 0.0, quadrantSpanDeg);
603 auto* endCapQuadrantShape =
new TGeoTubeSeg(
"RICH_SHIELD_ENDCAP_QUADRANT_SHAPE", shieldRMin, shieldRMax, endCapThicknessZ / 2.0, 0.0, quadrantSpanDeg);
606 auto* lateralStartWallShape =
new TGeoTubeSeg(
"RICH_SHIELD_LATERAL_START_WALL_SHAPE", innerGasRadius, outerGasRadius, shieldLengthZ / 2.0, 0.0, lateralStartWallSpanDeg);
609 auto* lateralEndWallShape =
new TGeoTubeSeg(
"RICH_SHIELD_LATERAL_END_WALL_SHAPE", innerGasRadius, outerGasRadius, shieldLengthZ / 2.0, 0.0, lateralEndWallSpanDeg);
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);
618 innerWallQuadrantVolume->SetLineColor(kOrange - 8);
619 outerWallQuadrantVolume->SetLineColor(kOrange - 8);
620 endCapPlusQuadrantVolume->SetLineColor(kOrange - 8);
621 endCapMinusQuadrantVolume->SetLineColor(kOrange - 8);
622 lateralStartWallVolume->SetLineColor(kOrange - 8);
623 lateralEndWallVolume->SetLineColor(kOrange - 8);
625 innerWallQuadrantVolume->SetTransparency(0);
626 outerWallQuadrantVolume->SetTransparency(0);
627 endCapPlusQuadrantVolume->SetTransparency(0);
628 endCapMinusQuadrantVolume->SetTransparency(0);
629 lateralStartWallVolume->SetTransparency(0);
630 lateralEndWallVolume->SetTransparency(0);
632 const double endCapCenterZ = shieldLengthZ / 2.0 + endCapThicknessZ / 2.0;
634 for (
int quadrant = 0; quadrant < 4; quadrant++) {
636 const double quadrantStartDeg = -45.0 +
static_cast<double>(quadrant) * 90.0 + vesselGapHalfDeg;
637 const double quadrantEndDeg = quadrantStartDeg + quadrantSpanDeg;
639 auto makeRotation = [&](
const char* prefix,
double angleDeg) {
640 auto* rotation =
new TGeoRotation(Form(
"%s_%d", prefix, quadrant));
641 rotation->RotateZ(angleDeg);
646 gasEnvelopeVolume->AddNode(innerWallQuadrantVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation(
"RICHInnerQuadrantRotation", quadrantStartDeg)));
648 gasEnvelopeVolume->AddNode(outerWallQuadrantVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation(
"RICHOuterQuadrantRotation", quadrantStartDeg)));
650 gasEnvelopeVolume->AddNode(endCapPlusQuadrantVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, endCapCenterZ, makeRotation(
"RICHEndCapPlusQuadrantRotation", quadrantStartDeg)));
652 gasEnvelopeVolume->AddNode(endCapMinusQuadrantVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, -endCapCenterZ, makeRotation(
"RICHEndCapMinusQuadrantRotation", quadrantStartDeg)));
654 gasEnvelopeVolume->AddNode(lateralStartWallVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation(
"RICHLateralStartRotation", quadrantStartDeg)));
656 gasEnvelopeVolume->AddNode(lateralEndWallVolume, quadrant + 1,
new TGeoCombiTrans(0.0, 0.0, 0.0, makeRotation(
"RICHLateralEndRotation", quadrantEndDeg - lateralEndWallSpanDeg)));
659 LOGP(info,
"RICH quadrant geometry: module pitch={} deg, module boundary half-gap={} deg, vessel half-gap={} deg", moduleDeltaPhi * radToDeg, moduleExtraPhi * radToDeg, vesselGapHalfDeg);
663 for (
int iRing{0}; iRing < richPars.nRings; ++iRing) {
664 if (!richPars.oddGeom && iRing == (richPars.nRings / 2)) {
671 richPars.radiatorThickness,
675 richPars.detectorThickness,
686 if (richPars.enableFWDRich) {
689 if (richPars.enableBWDRich) {
880 LOGP(info,
"Setting up {} layout for bRICH", richPars.oddGeom ?
"odd" :
"even");
882 bool isOdd = richPars.oddGeom;
884 mR0Tilt.resize(richPars.nRings);
885 mZ0Tilt.resize(richPars.nRings);
893 mVTile1.resize(richPars.nRings);
894 mVTile2.resize(richPars.nRings);
899 double mVal = TMath::Tan(0.0);
900 mThetaBi[richPars.nRings / 2] = TMath::ATan(mVal);
901 mR0Tilt[richPars.nRings / 2] = richPars.rMax;
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;
910 for (
int iRing{richPars.nRings / 2 + 1}; iRing < richPars.nRings; ++iRing) {
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)));
916 mThetaBi[iRing] = TMath::ATan(mVal);
917 mR0Tilt[iRing] = richPars.rMax - richPars.zBaseSize / 2.0 * sin(TMath::ATan(mVal));
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]);
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));
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));
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));
946 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() /
double(richPars.nTiles));
948 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * TMath::Tan(TMath::Pi() /
double(richPars.nTiles));
951 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() /
double(richPars.nTiles));
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");
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;
974 mVTile2[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
977 mVMirror2[iRing] = percentage * 2.0 * richPars.rMax * halfWidthFactor;
980 mVTile1[iRing] = percentage * 2.0 * richPars.rMin * halfWidthFactor;
997 if (richPars.useCylindricalAerogel) {
1002 const int nAerogelRows = richPars.oddGeom ? richPars.nRings : richPars.nRings - 1;
1004 if (nAerogelRows <= 0) {
1005 LOGP(fatal,
"Invalid number of cylindrical aerogel rows: {}", nAerogelRows);
1008 if (richPars.nTiles <= 0) {
1009 LOGP(fatal,
"Invalid number of aerogel tiles in phi: {}", richPars.nTiles);
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);
1015 double cylindricalAerogelTileSizeRPhi = 0.0;
1017 if (!richPars.flagUseQuadrants) {
1019 cylindricalAerogelTileSizeRPhi = 2.0 * richPars.rMin * TMath::Tan(TMath::Pi() /
static_cast<double>(richPars.nTiles));
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);
1026 LOGP(info,
"Cylindrical aerogel: rows={}, etaRef={}, tileSizeZ={} cm, tileSizeRPhi={} cm", nAerogelRows, richPars.cylindricalAerogelEtaRef, cylindricalAerogelTileSizeZ, cylindricalAerogelTileSizeRPhi);
1028 for (
int iRing = 0; iRing < richPars.nRings; iRing++) {
1029 mLAerogelZ[iRing] = cylindricalAerogelTileSizeZ;
1032 mVTile1[iRing] = cylindricalAerogelTileSizeRPhi;
1033 mVTile2[iRing] = cylindricalAerogelTileSizeRPhi;
1038 for (
size_t iRing{0}; iRing < richPars.nRings; ++iRing) {
1040 if (richPars.useCylindricalAerogel) {
1041 mR0Radiator[iRing] = richPars.rMin + richPars.radiatorThickness / 2.0;
1052 if (richPars.enableFWDRich) {
1053 LOGP(info,
"Setting up FWD RICH layout");
1057 richPars.zAerogelMin,
1058 richPars.zAerogelMax - richPars.zAerogelMin,
1060 richPars.zArgonMax - richPars.zArgonMin,
1061 richPars.zSiliconMin,
1062 richPars.zSiliconMax - richPars.zSiliconMin);
1064 if (richPars.enableBWDRich) {
1065 LOGP(info,
"Setting up BWD RICH layout");
1069 richPars.zAerogelMin,
1070 richPars.zAerogelMax - richPars.zAerogelMin,
1072 richPars.zArgonMax - richPars.zArgonMin,
1073 richPars.zSiliconMin,
1074 richPars.zSiliconMax - richPars.zSiliconMin);