@@ -35,6 +35,8 @@ const double Layer::sChipGap = 200.e-4; // cm
3535const double Layer::sModuleGap = 200 .e-4 ; // cm
3636const double Layer::sChipThickness = 300 .e-4 ; // cm
3737
38+ const double ITOFLayer::sStaggerRadialShift = 0.4 ; // cm
39+
3840const double OTOFLayer::sSubStavesDistanceY = 0.3 ; // cm
3941const double OTOFLayer::sSubStavesOverlapX = 1.1 ; // cm
4042
@@ -54,10 +56,12 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float
5456{
5557 const float Si_X0 = 9 .5f ; // cm, radiation length of silicon
5658 mLayerThickness = mX2X0 * Si_X0;
59+ const bool isSegmented = (layout == kBarrelSegmented || layout == kDiskSegmented || layout == kBarrelStaggered );
5760 std::string name = " " ;
5861 switch (layout) {
5962 case kBarrel :
6063 case kBarrelSegmented :
64+ case kBarrelStaggered :
6165 name = " barrel" ;
6266 mOuterRadius = mInnerRadius + mLayerThickness ;
6367 break ;
@@ -73,21 +77,28 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float
7377 if (mInnerRadius > mOuterRadius ) {
7478 LOG (fatal) << " Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm" ;
7579 }
76- if ((mStaves .first != 0 || mStaves .second != 0 .0f ) && (layout != kBarrelSegmented && layout != kDiskSegmented ) ) {
80+ if ((mStaves .first != 0 || mStaves .second != 0 .0f ) && !isSegmented ) {
7781 LOG (fatal) << " Invalid configuration: number of segments " << mStaves .first << " is set for non-segmented layout " << layout;
7882 }
79- if ((mStaves .first <= 1 || mStaves .second <= 0 .0f ) && (layout == kBarrelSegmented || layout == kDiskSegmented ) ) {
83+ if ((mStaves .first <= 1 || mStaves .second <= 0 .0f ) && isSegmented ) {
8084 LOG (fatal) << " Invalid configuration: number of segments " << mStaves .first << " must be positive for segmented layout " << layout;
8185 }
82- if (mModulesPerStave <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented ) ) {
86+ if (mModulesPerStave <= 0 && isSegmented ) {
8387 LOG (fatal) << " Invalid configuration: number of sensors per segment " << mModulesPerStave << " must be positive for segmented layout " << layout;
8488 }
85- if (std::abs (mTiltAngle ) > 0.1 && (layout != kBarrelSegmented && layout != kDiskSegmented ) ) {
89+ if (std::abs (mTiltAngle ) > 0.1 && !isSegmented ) {
8690 LOG (fatal) << " Invalid configuration: tilt angle " << mTiltAngle << " is set for non-segmented layout " << layout;
8791 }
88- if ((mTiltAngle < 0.0 || mTiltAngle > 90.0 ) && (layout == kBarrelSegmented || layout == kDiskSegmented ) ) {
92+ if ((mTiltAngle < 0.0 || mTiltAngle > 90.0 ) && isSegmented ) {
8993 LOG (fatal) << " Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees" ;
9094 }
95+ // Staggered and tilted ("turbo") layouts are alternatives
96+ if (layout == kBarrelStaggered && std::abs (mTiltAngle ) > 1 .e -6 ) {
97+ LOG (fatal) << " Invalid configuration: staggered layout requires tilt angle = 0, got " << mTiltAngle ;
98+ }
99+ if (layout == kBarrelStaggered && (mStaves .first % 2 ) != 0 ) {
100+ LOG (fatal) << " Invalid configuration: staggered layout requires an even number of staves, got " << mStaves .first ;
101+ }
91102 if (mSensorThickness < 0 .0f || mSensorThickness > mLayerThickness ) {
92103 LOG (fatal) << " Invalid configuration: sensor thickness " << mSensorThickness << " cm is out of range (0, " << mLayerThickness << " ) cm" ;
93104 }
@@ -177,28 +188,38 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume)
177188 motherVolume->AddNode (layerVol, 1 , nullptr );
178189 return ;
179190 }
180- case kBarrelSegmented : {
191+ case kBarrelSegmented :
192+ case kBarrelStaggered : {
193+ // kBarrelSegmented: staves tilted by mTiltAngle, all at the same radius ("turbo" layout)
194+ // kBarrelStaggered: untilted staves alternating between an inner and an outer radius
195+ const bool staggered = (mLayout == kBarrelStaggered );
196+ const double halfShift = staggered ? 0.5 * sStaggerRadialShift : 0 .; // cm
197+
181198 // First we create the volume for the whole layer, which will be used as mother volume for the segments
182199 const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius );
183200 const double staveSizeX = mStaves .second ; // cm
184201 const double staveSizeY = mOuterRadius - mInnerRadius ; // cm
185202 const double staveSizeZ = mZLength ; // cm
186203
187- // Build the mother layer tube from the exact inscribed/outscribed radii of a tilted stave rectangle.
204+ // Build the mother layer tube from the exact inscribed/circumscribed radii of the tilted stave rectangle.
205+ // For the staggered layout both the inner and the outer stave radii are taken into account.
188206 const double alpha = mTiltAngle * TMath::DegToRad ();
189- const double u0 = -avgRadius * std::cos (alpha);
190- const double v0 = avgRadius * std::sin (alpha);
191- const double uClamped = std::max (-0.5 * staveSizeY, std::min (0.5 * staveSizeY, u0));
192- const double vClamped = std::max (-0.5 * staveSizeX, std::min (0.5 * staveSizeX, v0));
193- const double radiusMin = std::hypot (uClamped - u0, vClamped - v0);
194-
195207 const double uCorners[4 ] = {-0.5 * staveSizeY, 0.5 * staveSizeY, 0.5 * staveSizeY, -0.5 * staveSizeY};
196208 const double vCorners[4 ] = {-0.5 * staveSizeX, -0.5 * staveSizeX, 0.5 * staveSizeX, 0.5 * staveSizeX};
209+ double radiusMin = 1 .e10 ;
197210 double radiusMax = 0.0 ;
198- for (int i = 0 ; i < 4 ; ++i) {
199- radiusMax = std::max (radiusMax, std::hypot (uCorners[i] - u0, vCorners[i] - v0));
211+ for (const int s : {-1 , 1 }) {
212+ const double rStave = avgRadius + s * halfShift;
213+ const double u0 = -rStave * std::cos (alpha);
214+ const double v0 = rStave * std::sin (alpha);
215+ const double uClamped = std::max (-0.5 * staveSizeY, std::min (0.5 * staveSizeY, u0));
216+ const double vClamped = std::max (-0.5 * staveSizeX, std::min (0.5 * staveSizeX, v0));
217+ radiusMin = std::min (radiusMin, std::hypot (uClamped - u0, vClamped - v0));
218+ for (int i = 0 ; i < 4 ; ++i) {
219+ radiusMax = std::max (radiusMax, std::hypot (uCorners[i] - u0, vCorners[i] - v0));
220+ }
200221 }
201- TGeoTube* layer = new TGeoTube (radiusMin, radiusMax, mZLength / 2 ); // cm, small margins to ensure staves are fully encapsulated in the layer volume
222+ TGeoTube* layer = new TGeoTube (radiusMin, radiusMax, mZLength / 2 ); // cm, encapsulates all staves
202223 TGeoVolume* layerVol = new TGeoVolume (mLayerName .c_str (), layer, medAir);
203224 setLayerStyle (layerVol);
204225
@@ -246,15 +267,17 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume)
246267 for (int i = 0 ; i < mStaves .first ; ++i) {
247268 LOGP (info, " iTOF: Creating stave {}/{} for layer {}" , i + 1 , mStaves .first , layerVol->GetName ());
248269 const double phi = TMath::TwoPi () * i / mStaves .first ;
249- const double x = avgRadius * TMath::Cos (phi);
250- const double y = avgRadius * TMath::Sin (phi);
270+ // staggered: even staves on the inner radius, odd staves on the outer radius
271+ const double rStave = avgRadius + (staggered ? ((i % 2 == 0 ) ? -halfShift : halfShift) : 0 .);
272+ const double x = rStave * TMath::Cos (phi);
273+ const double y = rStave * TMath::Sin (phi);
251274 auto * rotation = new TGeoRotation (Form (" segmentRot%d" , i + 1 ), phi * TMath::RadToDeg () + 90 + mTiltAngle , 0 , 0 );
252275 auto * transformation = new TGeoCombiTrans (x, y, 0 , rotation);
253276
254- LOGP (info, " Inserting Barrel {} in {} " , chipVol ->GetName (), layerVol->GetName ());
277+ LOGP (info, " Inserting Barrel {} in {} " , staveVol ->GetName (), layerVol->GetName ());
255278 layerVol->AddNode (staveVol, 1 + i, transformation);
256279 }
257- LOGP (info, " Inserting Barrel {} in {} at r={} cm" , layerVol->GetName (), motherVolume->GetName (), avgRadius);
280+ LOGP (info, " Inserting Barrel {} in {} at r={} cm{} " , layerVol->GetName (), motherVolume->GetName (), avgRadius, staggered ? " (staggered) " : " " );
258281 motherVolume->AddNode (layerVol, 1 , nullptr );
259282 return ;
260283 }
0 commit comments