97 double aerDetDistance,
99 const std::string motherName)
100 : mNTiles{nTilesPhi}, mPosId{rPosId}, mRadThickness{radThick}
102 TGeoManager* geoManager = gGeoManager;
103 TGeoVolume* motherVolume = geoManager->GetVolume(motherName.c_str());
107 "RICH: mother volume {} not found while creating ring {}",
114 const bool useCylindricalAerogel = richPars.useCylindricalAerogel;
116 TGeoMedium* medAerogel = gGeoManager->GetMedium(
"RCH_AEROGEL$");
118 LOGP(fatal,
"RICH: Aerogel medium not found");
121 TGeoMedium* medSi = gGeoManager->GetMedium(
"RCH_SILICON$");
123 LOGP(fatal,
"RICH: Silicon medium not found");
126 TGeoMedium* medCO2 = gGeoManager->GetMedium(
"RCH_CO2$");
128 LOGP(fatal,
"RICH: CO2 medium not found");
131 TGeoMedium* medFR4 = gGeoManager->GetMedium(
"RCH_FR4$");
133 LOGP(fatal,
"RICH: FR4 medium not found");
136 TGeoMedium* medAr = gGeoManager->GetMedium(
"RCH_ARGON$");
138 LOGP(fatal,
"RICH: Argon medium not found");
141 TGeoMedium* medAl = gGeoManager->GetMedium(
"RCH_ALUMINUM$");
143 LOGP(fatal,
"RICH: Aluminum medium not found");
146 TGeoMedium* medSiAbsorber = gGeoManager->GetMedium(
"RCH_SILICON_ABSORBER$");
147 if (!medSiAbsorber) {
148 LOGP(fatal,
"RICH: Passive silicon absorber medium not found");
151 TGeoMedium* medSilicone = gGeoManager->GetMedium(
"RCH_SILICONE$");
153 LOGP(fatal,
"RICH: Silicone medium not found");
156 TGeoMedium* medHTCC = gGeoManager->GetMedium(
"RCH_HTCC$");
158 LOGP(fatal,
"RICH: HTCC medium not found");
161 std::vector<TGeoArb8*> radiatorTiles(nTilesPhi), photoFrames(nTilesPhi), photoTiles(nTilesPhi), gasSectors(nTilesPhi);
162 LOGP(info,
"Creating ring: id: {} with {} tiles. ", rPosId, nTilesPhi);
163 LOGP(info,
"Rmin: {} Rmax: {} RadThick: {} RadYmin: {} RadYmax: {} RadZ: {} PhotThick: {} PhotYmin: {} PhotYmax: {} PhotZ: {}, zTransRad: {}, zTransPhot: {}, ThetaB: {}",
164 rMin, rMax, radThick, radYmin, radYmax, radZ, photThick, photYmin, photYmax, photZ, radRad0, photR0, thetaB);
167 const bool flagUseQuadrants = richPars.flagUseQuadrants;
168 if (flagUseQuadrants && (nTilesPhi <= 0 || nTilesPhi % 4 != 0)) {
169 LOGP(fatal,
"RICH quadrant geometry requires nTilesPhi to be positive and divisible by four; received {}", nTilesPhi);
171 const double regularDeltaPhi = 2.0 * TMath::Pi() /
static_cast<double>(nTilesPhi);
172 double moduleDeltaPhi = regularDeltaPhi;
173 double quadrantExtraPhi = 0.0;
175 if (flagUseQuadrants) {
176 const double totalBoundaryWidth = 2.0 * richPars.vesselThicknessShieldingLateral + richPars.vesselPhiGap;
177 if (totalBoundaryWidth >= 2.0 * rMin) {
178 LOGP(fatal,
"RICH quadrant boundary width {} cm is incompatible with rMin={} cm", totalBoundaryWidth, rMin);
180 moduleDeltaPhi = solveQuadrantDeltaPhi(nTilesPhi, rMin, totalBoundaryWidth);
182 quadrantExtraPhi = TMath::ASin(totalBoundaryWidth / (2.0 * rMin));
184 if (!(moduleDeltaPhi > 0.0)) {
185 LOGP(fatal,
"RICH ring {} could not solve the quadrant angular pitch", rPosId);
189 auto modulePhiRad = [&](
int moduleIndex) {
190 if (!flagUseQuadrants) {
192 return static_cast<double>(moduleIndex) * regularDeltaPhi;
194 return quadrantModulePhi(moduleIndex, nTilesPhi, moduleDeltaPhi, quadrantExtraPhi);
197 const double thetaBDeg = thetaB * 180.0 / TMath::Pi();
199 const double sipmActiveSizeZ = richPars.sipmActiveSizeZ;
202 const double sipmActiveSizeRPhi = flagUseQuadrants ? richPars.quadrantModuleSizeRPhi : richPars.sipmActiveSizeRPhi;
204 const double pcb1Thickness = richPars.pcb1Thickness;
205 const double coolingPlateThickness = richPars.coolingPlateThickness;
206 const double pcb2Thickness = richPars.pcb2Thickness;
207 const double pcb3Thickness = richPars.pcb3Thickness;
209 const double gapSiPMToPCB1 = richPars.gapSiPMToPCB1;
210 const double gapPCB1ToCoolingPlate = richPars.gapPCB1ToCoolingPlate;
211 const double gapCoolingPlateToPCB2 = richPars.gapCoolingPlateToPCB2;
212 const double gapPCB2ToPCB3 = richPars.gapPCB2ToPCB3;
214 const bool oddGeom = richPars.oddGeom;
215 const bool useRectangularModules = richPars.useRectangularModules;
217 const int nRings = richPars.nRings;
219 const double moduleClearanceZ = richPars.moduleClearanceZ;
220 const double moduleClearanceRPhi = richPars.moduleClearanceRPhi;
222 const double siliconeLayerThickness = richPars.siliconeLayerThickness;
223 const double activeSiliconThickness = richPars.activeSiliconThickness;
224 const double passiveSiliconThickness = photThick - activeSiliconThickness;
226 const double siliconFrontSurfaceOffset = -photThick / 2.0;
227 const double siliconeCenterOffset = siliconFrontSurfaceOffset - siliconeLayerThickness / 2.0;
228 const double activeSiliconCenterOffset = siliconFrontSurfaceOffset + activeSiliconThickness / 2.0;
229 const double passiveSiliconCenterOffset = siliconFrontSurfaceOffset + activeSiliconThickness + passiveSiliconThickness / 2.0;
231 if (siliconeLayerThickness <= 0.0) {
232 LOGP(fatal,
"RICH: siliconeLayerThickness must be positive");
235 if (activeSiliconThickness <= 0.0 || activeSiliconThickness >= photThick) {
236 LOGP(fatal,
"RICH: activeSiliconThickness={} cm must be larger than zero and smaller than detectorThickness={} cm", activeSiliconThickness, photThick);
239 if (passiveSiliconThickness <= 0.0) {
240 LOGP(fatal,
"RICH: passive silicon thickness must be positive");
243 if (moduleClearanceZ < 0.0 || moduleClearanceRPhi < 0.0) {
244 LOGP(fatal,
"RICH: module clearances cannot be negative");
247 if (photThick <= 0.0 || sipmActiveSizeZ <= 0.0 || sipmActiveSizeRPhi <= 0.0 || pcb1Thickness <= 0.0 || coolingPlateThickness <= 0.0 || pcb2Thickness <= 0.0 || pcb3Thickness <= 0.0) {
248 LOGP(fatal,
"RICH: SiPM and readout-stack dimensions must be positive");
251 if (gapSiPMToPCB1 < 0.0 || gapPCB1ToCoolingPlate < 0.0 || gapCoolingPlateToPCB2 < 0.0 || gapPCB2ToPCB3 < 0.0) {
252 LOGP(fatal,
"RICH: readout-stack gaps cannot be negative");
255 const double minimumFrameSizeRPhi = photYmin < photYmax ? photYmin : photYmax;
257 if (sipmActiveSizeZ > photZ || sipmActiveSizeRPhi > minimumFrameSizeRPhi) {
259 "RICH: rectangular module {} x {} cm2 does not fit inside the trapezoidal sector {} x [{}, {}] cm2 for ring {}. "
260 "For quadrant mode reduce: quadrantModuleSizeRPhi.",
261 sipmActiveSizeZ, sipmActiveSizeRPhi, photZ, photYmin, photYmax, rPosId);
265 const int nAerogelRows = oddGeom ? nRings : nRings - 1;
271 int aerogelRowIndex = rPosId;
272 if (!oddGeom && rPosId > nRings / 2) {
276 const double cylindricalAerogelCenterZ = -0.5 *
static_cast<double>(nAerogelRows) * radZ + 0.5 * radZ +
static_cast<double>(aerogelRowIndex) * radZ;
278 int radTileCount{0}, photTileCount{0};
280 if (flagUseQuadrants) {
281 LOGP(info,
"RICH ring {} quadrant placement: deltaPhi={} deg, boundary half-gap={} deg", rPosId, moduleDeltaPhi * 180.0 / TMath::Pi(), quadrantExtraPhi * 180.0 / TMath::Pi());
285 for (
auto& radiatorTile : radiatorTiles) {
290 if (useCylindricalAerogel) {
292 const double cylindricalTileSizeZ = radZ - moduleClearanceZ;
293 const double cylindricalTileYmin = radYmin - moduleClearanceRPhi;
294 const double cylindricalTileYmax = radYmax - moduleClearanceRPhi;
295 if (cylindricalTileSizeZ <= 0.0 || cylindricalTileYmin <= 0.0 || cylindricalTileYmax <= 0.0) {
296 LOGP(fatal,
"RICH: cylindrical-aerogel clearances are larger than the tile dimensions for ring {}", rPosId);
298 radiatorTile =
new TGeoArb8(radThick / 2);
299 radiatorTile->SetVertex(0, cylindricalTileSizeZ / 2, -cylindricalTileYmin / 2);
300 radiatorTile->SetVertex(1, -cylindricalTileSizeZ / 2, -cylindricalTileYmax / 2);
301 radiatorTile->SetVertex(2, -cylindricalTileSizeZ / 2, cylindricalTileYmax / 2);
302 radiatorTile->SetVertex(3, cylindricalTileSizeZ / 2, cylindricalTileYmin / 2);
303 radiatorTile->SetVertex(4, cylindricalTileSizeZ / 2, -cylindricalTileYmin / 2);
304 radiatorTile->SetVertex(5, -cylindricalTileSizeZ / 2, -cylindricalTileYmax / 2);
305 radiatorTile->SetVertex(6, -cylindricalTileSizeZ / 2, cylindricalTileYmax / 2);
306 radiatorTile->SetVertex(7, cylindricalTileSizeZ / 2, cylindricalTileYmin / 2);
309 radiatorTile =
new TGeoArb8(radThick / 2);
310 radiatorTile->SetVertex(0, radZ / 2, -radYmin / 2);
311 radiatorTile->SetVertex(1, -radZ / 2, -radYmax / 2);
312 radiatorTile->SetVertex(2, -radZ / 2, radYmax / 2);
313 radiatorTile->SetVertex(3, radZ / 2, radYmin / 2);
314 radiatorTile->SetVertex(4, radZ / 2, -radYmin / 2);
315 radiatorTile->SetVertex(5, -radZ / 2, -radYmax / 2);
316 radiatorTile->SetVertex(6, -radZ / 2, radYmax / 2);
317 radiatorTile->SetVertex(7, radZ / 2, radYmin / 2);
320 TGeoVolume* radiatorTileVol =
new TGeoVolume(Form(
"radTile_%d_%d", rPosId, radTileCount), radiatorTile, medAerogel);
321 radiatorTileVol->SetLineColor(kBlue - 9);
322 radiatorTileVol->SetLineWidth(1);
327 const double phiRad = modulePhiRad(radTileCount);
328 const double phiDeg = phiRad * 180.0 / TMath::Pi();
330 auto* rotRadiator =
new TGeoRotation(Form(
"radTileRotation_%d_%d", radTileCount, rPosId));
332 if (useCylindricalAerogel) {
336 rotRadiator->RotateY(90.0);
339 rotRadiator->RotateY(90.0 - thetaBDeg);
343 rotRadiator->RotateZ(phiDeg);
345 const double radiatorCenterZ = useCylindricalAerogel ? cylindricalAerogelCenterZ : radRad0 * TMath::Tan(thetaB);
347 auto* rotTransRadiator =
new TGeoCombiTrans(radRad0 * TMath::Cos(phiRad), radRad0 * TMath::Sin(phiRad), radiatorCenterZ, rotRadiator);
349 motherVolume->AddNode(radiatorTileVol, 1, rotTransRadiator);
354 if (!useRectangularModules) {
355 for (
auto& photoTile : photoTiles) {
356 const double phiRad = modulePhiRad(photTileCount);
357 const double phiDeg = phiRad * 180.0 / TMath::Pi();
359 photoTile =
new TGeoArb8(photThick / 2);
360 photoTile->SetVertex(0, photZ / 2, -photYmin / 2);
361 photoTile->SetVertex(1, -photZ / 2, -photYmax / 2);
362 photoTile->SetVertex(2, -photZ / 2, photYmax / 2);
363 photoTile->SetVertex(3, photZ / 2, photYmin / 2);
364 photoTile->SetVertex(4, photZ / 2, -photYmin / 2);
365 photoTile->SetVertex(5, -photZ / 2, -photYmax / 2);
366 photoTile->SetVertex(6, -photZ / 2, photYmax / 2);
367 photoTile->SetVertex(7, photZ / 2, photYmin / 2);
370 photoTileVol->SetLineColor(kOrange + 2);
371 photoTileVol->SetLineWidth(1);
373 auto* rotPhoto =
new TGeoRotation(Form(
"photoTileRotation_%d_%d", photTileCount, rPosId));
374 rotPhoto->RotateY(90.0 - thetaBDeg);
376 rotPhoto->RotateZ(phiDeg);
381 auto* rotTransPhoto =
new TGeoCombiTrans(photR0 * TMath::Cos(phiRad), photR0 * TMath::Sin(phiRad), photR0 * TMath::Tan(thetaB), rotPhoto);
383 motherVolume->AddNode(photoTileVol, 1, rotTransPhoto);
389 for (
auto& photoTile : photoTiles) {
392 const double phiRad = modulePhiRad(photTileCount);
393 const double phiDeg = phiRad * 180.0 / TMath::Pi();
395 const double photoCenterR = photR0;
396 const double photoCenterZ = photR0 * TMath::Tan(thetaB);
399 const double normalRadial = TMath::Cos(thetaB);
400 const double normalZ = TMath::Sin(thetaB);
402 auto makeProjectiveRotation = [&](
const char* prefix) {
403 auto* rotation =
new TGeoRotation(Form(
"%sRotation_%d_%d", prefix, photTileCount, rPosId));
404 rotation->RotateY(90.0 - thetaBDeg);
405 rotation->RotateZ(phiDeg);
409 const double frameSizeZ = photZ - moduleClearanceZ;
410 const double frameYmin = photYmin - moduleClearanceRPhi;
411 const double frameYmax = photYmax - moduleClearanceRPhi;
413 auto makeFrameFootprint = [&](
double thickness) {
414 auto* shape =
new TGeoArb8(thickness / 2.0);
415 shape->SetVertex(0, frameSizeZ / 2.0, -frameYmin / 2.0);
416 shape->SetVertex(1, -frameSizeZ / 2.0, -frameYmax / 2.0);
417 shape->SetVertex(2, -frameSizeZ / 2.0, frameYmax / 2.0);
418 shape->SetVertex(3, frameSizeZ / 2.0, frameYmin / 2.0);
419 shape->SetVertex(4, frameSizeZ / 2.0, -frameYmin / 2.0);
420 shape->SetVertex(5, -frameSizeZ / 2.0, -frameYmax / 2.0);
421 shape->SetVertex(6, -frameSizeZ / 2.0, frameYmax / 2.0);
422 shape->SetVertex(7, frameSizeZ / 2.0, frameYmin / 2.0);
426 auto makeRectangularFootprint = [&](
double thickness) {
427 auto* shape =
new TGeoArb8(thickness / 2.0);
428 shape->SetVertex(0, sipmActiveSizeZ / 2.0, -sipmActiveSizeRPhi / 2.0);
429 shape->SetVertex(1, -sipmActiveSizeZ / 2.0, -sipmActiveSizeRPhi / 2.0);
430 shape->SetVertex(2, -sipmActiveSizeZ / 2.0, sipmActiveSizeRPhi / 2.0);
431 shape->SetVertex(3, sipmActiveSizeZ / 2.0, sipmActiveSizeRPhi / 2.0);
432 shape->SetVertex(4, sipmActiveSizeZ / 2.0, -sipmActiveSizeRPhi / 2.0);
433 shape->SetVertex(5, -sipmActiveSizeZ / 2.0, -sipmActiveSizeRPhi / 2.0);
434 shape->SetVertex(6, -sipmActiveSizeZ / 2.0, sipmActiveSizeRPhi / 2.0);
435 shape->SetVertex(7, sipmActiveSizeZ / 2.0, sipmActiveSizeRPhi / 2.0);
439 auto addReadoutLayer = [&](
const char* prefix,
444 bool useRectangularFootprint) {
445 auto* shape = useRectangularFootprint ? makeRectangularFootprint(thickness) : makeFrameFootprint(thickness);
446 auto* volume =
new TGeoVolume(Form(
"%s_%d_%d", prefix, rPosId, photTileCount), shape, medium);
447 volume->SetLineColor(lineColor);
448 volume->SetLineWidth(1);
449 const double layerCenterR = photoCenterR + centerOffset * normalRadial;
450 const double layerCenterZ = photoCenterZ + centerOffset * normalZ;
451 auto* transform =
new TGeoCombiTrans(layerCenterR * TMath::Cos(phiRad), layerCenterR * TMath::Sin(phiRad), layerCenterZ, makeProjectiveRotation(prefix));
452 motherVolume->AddNode(volume, 1, transform);
460 photoFrames[photTileCount] = makeFrameFootprint(photThick);
461 auto* photoFrameVol =
new TGeoVolume(Form(
"photoFrame_%d_%d", rPosId, photTileCount), photoFrames[photTileCount], medSi);
462 photoFrameVol->SetLineColor(kGray + 2);
463 photoFrameVol->SetLineWidth(1);
490 addReadoutLayer(
"siliconeLayer", siliconeLayerThickness, siliconeCenterOffset, medSilicone, kOrange + 2, useRectangularModules);
493 photoTile = makeRectangularFootprint(activeSiliconThickness);
495 const double activeSiliconCenterR = photoCenterR + activeSiliconCenterOffset * normalRadial;
496 const double activeSiliconCenterZ = photoCenterZ + activeSiliconCenterOffset * normalZ;
497 auto* rotTransPhoto =
new TGeoCombiTrans(activeSiliconCenterR * TMath::Cos(phiRad), activeSiliconCenterR * TMath::Sin(phiRad), activeSiliconCenterZ, makeProjectiveRotation(
"photoTile"));
498 motherVolume->AddNode(photoTileVol, 1, rotTransPhoto);
501 addReadoutLayer(
"siliconAbsorber", passiveSiliconThickness, passiveSiliconCenterOffset, medSiAbsorber, kBlue + 1,
true);
508 double outerSurfaceOffset = photThick / 2.0;
510 outerSurfaceOffset += gapSiPMToPCB1;
511 const double pcb1CenterOffset = outerSurfaceOffset + pcb1Thickness / 2.0;
512 addReadoutLayer(
"pcb1", pcb1Thickness, pcb1CenterOffset, medFR4, kGreen + 1, useRectangularModules);
513 outerSurfaceOffset += pcb1Thickness;
515 outerSurfaceOffset += gapPCB1ToCoolingPlate;
516 const double coolingPlateCenterOffset = outerSurfaceOffset + coolingPlateThickness / 2.0;
517 addReadoutLayer(
"coolingPlate", coolingPlateThickness, coolingPlateCenterOffset, medHTCC, kRed, useRectangularModules);
518 outerSurfaceOffset += coolingPlateThickness;
520 outerSurfaceOffset += gapCoolingPlateToPCB2;
521 const double pcb2CenterOffset = outerSurfaceOffset + pcb2Thickness / 2.0;
522 addReadoutLayer(
"pcb2", pcb2Thickness, pcb2CenterOffset, medFR4, kGreen + 2, useRectangularModules);
523 outerSurfaceOffset += pcb2Thickness;
525 outerSurfaceOffset += gapPCB2ToPCB3;
526 const double pcb3CenterOffset = outerSurfaceOffset + pcb3Thickness / 2.0;
527 addReadoutLayer(
"pcb3", pcb3Thickness, pcb3CenterOffset, medFR4, kGreen + 3, useRectangularModules);