Skip to content

Commit aa96c1a

Browse files
authored
IOTOF: align geometry to specs (#15414)
- add macro to draw geometry - streamline setup of IOTOF active layers
1 parent 084c366 commit aa96c1a

4 files changed

Lines changed: 156 additions & 19 deletions

File tree

Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@
1111

1212
o2_add_test_root_macro(defineIOTOFGeo.C
1313
LABELS alice3)
14+
15+
o2_add_test_root_macro(drawTOFGeometry.C
16+
LABELS alice3)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 "IOTOFBase/GeometryTGeo.h"
13+
#include "IOTOFSimulation/Layer.h"
14+
15+
#include <TCanvas.h>
16+
#include <TGeoManager.h>
17+
#include <TGeoMaterial.h>
18+
#include <TGeoMatrix.h>
19+
#include <TGeoVolume.h>
20+
#include <TStyle.h>
21+
22+
#include <iostream>
23+
24+
namespace
25+
{
26+
void ensureMedium(const char* name, int id, double a, double z, double density)
27+
{
28+
if (!gGeoManager->GetMedium(name)) {
29+
auto* mat = new TGeoMaterial(name, a, z, density);
30+
new TGeoMedium(name, id, mat);
31+
}
32+
}
33+
34+
void prepareMinimalMedia()
35+
{
36+
ensureMedium("VACUUM$", 0, 1., 1., 1.e-16);
37+
ensureMedium("TF3_AIR$", 1, 14.61, 7.3, 1.20479e-3);
38+
ensureMedium("TF3_SILICON$", 3, 28.086, 14., 2.33);
39+
}
40+
} // namespace
41+
42+
void drawTOFGeometry(double x2x0 = 0.02,
43+
double sensorThickness = 0.005,
44+
bool checkOverlaps = true,
45+
double overlapToleranceCm = 0.01)
46+
{
47+
gStyle->SetOptStat(0);
48+
49+
if (gGeoManager) {
50+
delete gGeoManager;
51+
}
52+
53+
auto* geo = new TGeoManager("IOTOFGeomFromLayer", "Geometry built from Layer.h classes");
54+
prepareMinimalMedia();
55+
56+
auto* top = geo->MakeBox("TOP", geo->GetMedium("VACUUM$"), 1200., 1200., 1200.);
57+
geo->SetTopVolume(top);
58+
59+
auto* mother = new TGeoVolumeAssembly("IOTOFMacroVol");
60+
top->AddNode(mother, 1, new TGeoTranslation(0., 0., 0.));
61+
62+
// Build using the same classes and createLayer() used by detector geometry code.
63+
o2::iotof::ITOFLayer itof(o2::iotof::GeometryTGeo::getITOFLayerPattern(),
64+
21.f, 0.f, 129.f, 0.f, x2x0,
65+
o2::iotof::Layer::kBarrelSegmented,
66+
24, 5.42, 3.0, 10, sensorThickness);
67+
68+
o2::iotof::OTOFLayer otof(o2::iotof::GeometryTGeo::getOTOFLayerPattern(),
69+
92.f, 0.f, 680.f, 0.f, x2x0,
70+
o2::iotof::Layer::kBarrelSegmented,
71+
62, 9.74, 5.0, 54, sensorThickness);
72+
73+
itof.createLayer(mother);
74+
otof.createLayer(mother);
75+
76+
geo->CloseGeometry();
77+
78+
std::cout << "Built geometry from Layer.h classes with x2x0=" << x2x0
79+
<< " and sensorThickness=" << sensorThickness << " cm\n";
80+
std::cout << "ITOF sensitive volumes: " << o2::iotof::ITOFLayer::mRegister.size() << "\n";
81+
std::cout << "OTOF sensitive volumes: " << o2::iotof::OTOFLayer::mRegister.size() << "\n";
82+
83+
if (checkOverlaps) {
84+
std::cout << "Checking overlaps with tolerance=" << overlapToleranceCm << " cm\n";
85+
geo->CheckOverlaps(overlapToleranceCm);
86+
geo->PrintOverlaps();
87+
}
88+
89+
top->Draw("ogl");
90+
}

Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,28 +200,47 @@ void Detector::defineSensitiveVolumes()
200200
TGeoManager* geoManager = gGeoManager;
201201
TGeoVolume* v;
202202

203-
// The names of the IOTOF sensitive volumes have the format: IOTOFLayer(0...mLayers.size()-1)
204203
auto& iotofPars = IOTOFBaseParam::Instance();
205-
if (iotofPars.enableInnerTOF) {
204+
const bool itof = iotofPars.enableInnerTOF;
205+
const bool otof = iotofPars.enableOuterTOF;
206+
bool ftof = iotofPars.enableForwardTOF;
207+
bool btof = iotofPars.enableBackwardTOF;
208+
const std::string pattern = iotofPars.detectorPattern;
209+
if (pattern == "") {
210+
LOG(info) << "Default pattern";
211+
} else if (pattern == "v3b") {
212+
ftof = false;
213+
btof = false;
214+
} else if (pattern == "v3b1a") {
215+
} else if (pattern == "v3b1b") {
216+
} else if (pattern == "v3b2a") {
217+
} else if (pattern == "v3b2b") {
218+
} else if (pattern == "v3b3") {
219+
} else {
220+
LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting";
221+
}
222+
223+
// The names of the IOTOF sensitive volumes have the format: IOTOFLayer(0...mLayers.size()-1)
224+
if (itof) {
206225
for (const std::string& itofSensor : ITOFLayer::mRegister) {
207226
v = geoManager->GetVolume(itofSensor.c_str());
208227
LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName());
209228
AddSensitiveVolume(v);
210229
}
211230
}
212-
if (iotofPars.enableOuterTOF) {
231+
if (otof) {
213232
for (const std::string& otofSensor : OTOFLayer::mRegister) {
214233
v = geoManager->GetVolume(otofSensor.c_str());
215234
LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName());
216235
AddSensitiveVolume(v);
217236
}
218237
}
219-
if (iotofPars.enableForwardTOF) {
238+
if (ftof) {
220239
v = geoManager->GetVolume(GeometryTGeo::getFTOFSensorPattern());
221240
LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName());
222241
AddSensitiveVolume(v);
223242
}
224-
if (iotofPars.enableBackwardTOF) {
243+
if (btof) {
225244
v = geoManager->GetVolume(GeometryTGeo::getBTOFSensorPattern());
226245
LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName());
227246
AddSensitiveVolume(v);

Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,24 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume)
296296
case kBarrelSegmented: {
297297
// First we create the volume for the whole layer, which will be used as mother volume for the segments
298298
const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius);
299-
const double staveSizeX = mStaves.second; // cm
300-
const double staveSizeY = mOuterRadius - mInnerRadius; // cm
301-
const double staveSizeZ = mZLength; // cm
302-
const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves
303-
const double radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt); // we increase the outer radius to account for the tilt of the staves
304-
const double radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); // we decrease the inner radius to account for the tilt of the staves
299+
const double staveSizeX = mStaves.second; // cm, tangential stave size
300+
const double staveSizeY = mOuterRadius - mInnerRadius; // cm, radial stave size
301+
const double staveSizeZ = mZLength; // cm
302+
303+
// Build the mother layer tube from the exact inscribed/outscribed radii of a tilted stave rectangle.
304+
const double alpha = mTiltAngle * TMath::DegToRad();
305+
const double u0 = -avgRadius * std::cos(alpha);
306+
const double v0 = avgRadius * std::sin(alpha);
307+
const double uClamped = std::max(-0.5 * staveSizeY, std::min(0.5 * staveSizeY, u0));
308+
const double vClamped = std::max(-0.5 * staveSizeX, std::min(0.5 * staveSizeX, v0));
309+
const double radiusMin = std::hypot(uClamped - u0, vClamped - v0);
310+
311+
const double uCorners[4] = {-0.5 * staveSizeY, 0.5 * staveSizeY, 0.5 * staveSizeY, -0.5 * staveSizeY};
312+
const double vCorners[4] = {-0.5 * staveSizeX, -0.5 * staveSizeX, 0.5 * staveSizeX, 0.5 * staveSizeX};
313+
double radiusMax = 0.0;
314+
for (int i = 0; i < 4; ++i) {
315+
radiusMax = std::max(radiusMax, std::hypot(uCorners[i] - u0, vCorners[i] - v0));
316+
}
305317
TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2);
306318
TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir);
307319
setLayerStyle(layerVol);
@@ -312,10 +324,21 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume)
312324
setStaveStyle(staveVol);
313325

314326
// Now we create the volume for a single module (sensor + chip)
315-
const int modulesPerStaveX = 1; // we assume that each stave is divided in 2 modules along the x direction
316-
const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm
317-
const double moduleSizeY = staveSizeY; // cm
318-
const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm
327+
// oTOF V2 is a 2xN matrix of modules per stave with overlap along z.
328+
const int modulesPerStaveX = 2;
329+
if (mModulesPerStave % modulesPerStaveX != 0) {
330+
LOG(fatal) << "Invalid oTOF module layout: total modules per stave " << mModulesPerStave
331+
<< " is not divisible by modulesPerStaveX=" << modulesPerStaveX;
332+
}
333+
const int modulesPerStaveZ = mModulesPerStave / modulesPerStaveX;
334+
const double moduleOverlapZ = 0.7; // cm, 7 mm longitudinal overlap from oTOF V2 specs
335+
const double moduleSizeX = staveSizeX / modulesPerStaveX;
336+
const double moduleSizeY = staveSizeY;
337+
const double moduleSizeZ = (staveSizeZ + (modulesPerStaveZ - 1) * moduleOverlapZ) / modulesPerStaveZ;
338+
const double modulePitchZ = moduleSizeZ - moduleOverlapZ;
339+
if (modulePitchZ <= 0.0) {
340+
LOG(fatal) << "Invalid oTOF module overlap " << moduleOverlapZ << " cm for module size " << moduleSizeZ << " cm";
341+
}
319342
TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5);
320343
TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir);
321344
setModuleStyle(moduleVol);
@@ -363,10 +386,12 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume)
363386

364387
// Now we build a stave from modules
365388
for (int i = 0; i < modulesPerStaveX; ++i) {
366-
for (int j = 0; j < mModulesPerStave; ++j) {
367-
LOGP(info, "oTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave);
368-
auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ);
369-
staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation);
389+
for (int j = 0; j < modulesPerStaveZ; ++j) {
390+
LOGP(info, "oTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, modulesPerStaveZ);
391+
const double tx = (i + 0.5) * moduleSizeX - 0.5 * staveSizeX;
392+
const double tz = -0.5 * staveSizeZ + 0.5 * moduleSizeZ + j * modulePitchZ;
393+
auto* translation = new TGeoTranslation(tx, 0, tz);
394+
staveVol->AddNode(moduleVol, 1 + i * modulesPerStaveZ + j, translation);
370395
}
371396
}
372397

0 commit comments

Comments
 (0)