diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index f3fd9109b839b..9b51fa911ac79 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -21,11 +21,11 @@ namespace iotof { struct ChipSpecifics { - int NCols = 129; - int NRows = 271; + int NCols = 120; + int NRows = 240; float PitchCol = 250.00e-4; float PitchRow = 100.00e-4; - float PassiveEdgeReadOut = 0.; + float PassiveEdgeReadOut = 0.2; float PassiveEdgeTop = 0.; float PassiveEdgeSide = 0.; float PixelPassiveEdgeX = 0.; @@ -49,12 +49,25 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableOuterTOF = true; // Enable Outer TOF layer bool enableForwardTOF = false; // Enable Forward TOF layer bool enableBackwardTOF = false; // Enable Backward TOF layer + bool itofTurbo = true; // Segmented ITOF turbo or staggered std::string detectorPattern = ""; // Layouts of the detector bool segmentedInnerTOF = true; // If the inner TOF layer is segmented bool segmentedOuterTOF = true; // If the outer TOF layer is segmented float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment float sensorThickness = 0.0050f; // thickness of the sensor in cm, for all layers for the moment, the default is set to 50 microns + // ITOF segmentation parameters + float itofRadius = 21.38f; // ITOF radius (cm) + float itofLength = 126.82f; // ITOF length (cm) + int itofNStaves = 28; // ITOF number of staves + float itofStaveTilt = 6.63; // ITOF stave tilt angle (deg) + + // OTOF segmentation parameters + float otofRadius = 92.f; // OTOF radius (cm) + float otofLength = 688.54f; // OTOF length (cm) + int otofNStaves = 62; // OTOF number of staves + float otofStaveTilt = 5; // OTOF stave tilt angle (deg) + O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C index f2631fc826292..7fda3393d03e3 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C @@ -240,21 +240,21 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi auto canvXY = new TCanvas("canvXY", "", 1600, 800); canvXY->Divide(2, 1); canvXY->cd(1); - nt->Draw("y:x>>h_y_vs_x_IOTOF(1000, -100, 100, 1000, -100, 100)", "id >= 0 && id < 55488", "colz"); + nt->Draw("y:x>>h_y_vs_x_IOTOF(1000, -100, 100, 1000, -100, 100)", "id >= 0 && id < 58896", "colz"); canvXY->cd(2); - nt->Draw("y:z>>h_y_vs_z_IOTOF(1000, -400, 400, 1000, -100, 100)", "id >= 0 && id < 55488", "colz"); + nt->Draw("y:z>>h_y_vs_z_IOTOF(1000, -400, 400, 1000, -100, 100)", "id >= 0 && id < 58896", "colz"); canvXY->SaveAs("tf3digits_y_vs_x_vs_z.pdf"); // z distributions auto canvZ = new TCanvas("canvZ", "", 800, 800); canvZ->cd(); - nt->Draw("z>>h_z_IOTOF(500, -70, 70)", "id >= 0 && id < 55488 "); + nt->Draw("z>>h_z_IOTOF(500, -70, 70)", "id >= 0 && id < 58896 "); canvZ->SaveAs("tf3digits_z.pdf"); // dz distributions (difference between local position of digits and hits in x and z) auto canvdZ = new TCanvas("canvdZ", "", 800, 800); canvdZ->cd(); - nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 0 && id < 55488 "); + nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 0 && id < 58896 "); canvdZ->SaveAs("tf3digits_dz.pdf"); canvdZ->SaveAs("tf3digits_dz.root"); @@ -264,13 +264,13 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); canvdXdZ->Divide(2, 1); canvdXdZ->cd(1); - nt->Draw(Form("dx:dz>>h_dx_vs_dz_ITOF(600, -%f, %f, 600, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 0 && id < 1920", "colz"); + nt->Draw(Form("dx:dz>>h_dx_vs_dz_ITOF(600, -%f, %f, 600, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 0 && id < 2352", "colz"); addTLines(chipInfo.PitchRow, chipInfo.PitchCol); auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ITOF"); Info("ITOF", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("ITOF", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->cd(2); - nt->Draw(Form("dx:dz>>h_dx_vs_dz_OTOF(600, -%f, %f, 600, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 1920 && id < 55488", "colz"); + nt->Draw(Form("dx:dz>>h_dx_vs_dz_OTOF(600, -%f, %f, 600, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 2352 && id < 58896", "colz"); addTLines(chipInfo.PitchRow, chipInfo.PitchCol); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OTOF"); Info("OTOF", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); @@ -283,13 +283,13 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi canvdXdZHit->Divide(2, 1); canvdXdZHit->cd(1); LOG(info) << "dxH, dzH"; - nt2->Draw(Form("dxH:dzH>>h_dxH_vs_dzH_ITOF(300, -%f, %f, 300, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 0 && id < 1920", "colz"); + nt2->Draw(Form("dxH:dzH>>h_dxH_vs_dzH_ITOF(300, -%f, %f, 300, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 0 && id < 2352", "colz"); addTLines(chipInfo.PitchRow, chipInfo.PitchCol); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ITOF"); Info("ITOF", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("ITOF", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->cd(2); - nt2->Draw(Form("dxH:dzH>>h_dxH_vs_dzH_OTOF(300, -%f, %f, 300, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 1920 && id < 55488", "colz"); + nt2->Draw(Form("dxH:dzH>>h_dxH_vs_dzH_OTOF(300, -%f, %f, 300, -%f, %f)", canvaEdgeRow, canvaEdgeCol, canvaEdgeRow, canvaEdgeCol), "id >= 2352 && id < 58896", "colz"); addTLines(chipInfo.PitchRow, chipInfo.PitchCol); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OTOF"); Info("OTOF", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt index edf92ea533625..10d97cb1ac39f 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt @@ -11,6 +11,7 @@ o2_add_library(IOTOFSimulation SOURCES src/Layer.cxx + src/TF3Chip.cxx src/Chip.cxx src/Detector.cxx src/Digitizer.cxx @@ -23,6 +24,7 @@ o2_add_library(IOTOFSimulation o2_target_root_dictionary(IOTOFSimulation HEADERS include/IOTOFSimulation/Detector.h include/IOTOFSimulation/Chip.h + include/IOTOFSimulation/TF3Chip.h include/IOTOFSimulation/Layer.h include/IOTOFSimulation/Digitizer.h include/IOTOFSimulation/DPLDigitizerParam.h diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index dc9fedf439a11..dca1aa349715a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -34,7 +34,7 @@ class Layer auto getZLength() const { return mZLength; } auto getZOffset() const { return mZOffset; } auto getx2X0() const { return mX2X0; } - auto getChipThickness() const { return mChipThickness; } + auto getChipThickness() const { return mLayerThickness; } auto getName() const { return mLayerName; } auto getLayout() const { return mLayout; } auto getSegments() const { return mStaves; } @@ -42,6 +42,7 @@ class Layer static constexpr int kDisk = 1; static constexpr int kBarrelSegmented = 2; static constexpr int kDiskSegmented = 3; + static constexpr int kBarrelStaggered = 4; virtual void createLayer(TGeoVolume* motherVolume) {}; @@ -52,13 +53,19 @@ class Layer float mZLength; float mZOffset{0.f}; // Of use when fwd layers float mX2X0; - float mChipThickness; // Thickness of the chip in cm, derived from mX2X0 and the radiation length of silicon + float mLayerThickness; // Thickness of the layer in cm, derived from mX2X0 and the radiation length of silicon float mSensorThickness; // Thickness of the sensor in cm, to be subtracted from the chip thickness to get the total module thickness int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) // To be used only in case of the segmented layout, to define the number of staves in phi (for barrel) or in r (for disk) std::pair mStaves{0, 0.0f}; // Number and size of staves in phi (for barrel) or in r (for disk) in case of segmented layout int mModulesPerStave{0}; // Number of modules along a stave double mTiltAngle{0.0}; // Tilt angle in degrees to be applied as a rotation around the local center of the stave + + static const int sChipsPerRow; // number of chips per row in module + static const int sChipsPerCol; // number of chips per col in module + static const double sChipGap; // gap between chips in module + static const double sModuleGap; // gap between modules in stave + static const double sChipThickness; // full thickness of the chip }; class ITOFLayer : public Layer @@ -67,6 +74,10 @@ class ITOFLayer : public Layer using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; static std::vector mRegister; + + static const double sStaggerRadialShift; + + private: }; class OTOFLayer : public Layer @@ -75,6 +86,9 @@ class OTOFLayer : public Layer using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; static std::vector mRegister; + + static const double sSubStavesDistanceY; + static const double sSubStavesOverlapX; }; class FTOFLayer : public Layer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/TF3Chip.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/TF3Chip.h new file mode 100644 index 0000000000000..69fdf884ba222 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/TF3Chip.h @@ -0,0 +1,60 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_IOTOF_TF3CHIP_H_ +#define ALICEO2_IOTOF_TF3CHIP_H_ + +#include // for gGeoManager +#include "Rtypes.h" // for Int_t, Double_t, Bool_t, UInt_t, etc + +class TGeoVolume; +class TGeoManager; + +namespace o2 +{ + +namespace iotof +{ + +/// TF3Chip class creates a TGeoVolume representing the TF3 Chip + +class TF3Chip +{ + public: + TF3Chip() = default; + ~TF3Chip() = default; + + /// Creates the TF3Chip + /// Returns the chip as a TGeoVolume + /// \param yc Y chip half lengths + /// \param ys sensor half thickness + /// \param chipName sensName default volume names + /// \param dummy if true creates a dummy air volume (for material budget studies) + /// \param mgr The GeoManager (used only to get the proper material) + static TGeoVolume* createChip(Double_t yc, Double_t ys, + char const* chipName = "TF3Chip", char const* sensName = "TF3Sensor", + Bool_t dummy = kFALSE, const TGeoManager* mgr = gGeoManager); + + static void createMaterials(Int_t id, Int_t fieldType, Float_t maxField); + + static const double getChipSizeX() { return sChipSizeX; }; + static const double getChipSizeY() { return sChipSizeZ; }; + + static const double sChipSizeX; + static const double sChipSizeZ; + + ClassDefNV(TF3Chip, 0); // TF3Chip geometry +}; + +} // namespace iotof +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 34cff3fe0b4a4..95e3f95929bf6 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -61,8 +61,9 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const float x2x0, const float sensorThickness) { - std::pair dInnerTof = {21.f, 129.f}; // Radius and length - std::pair dOuterTof = {92.f, 680.f}; // Radius and length + auto& iotofPars = IOTOFBaseParam::Instance(); + std::pair dInnerTof = {iotofPars.itofRadius, iotofPars.itofLength}; // Radius and length + std::pair dOuterTof = {iotofPars.otofRadius, iotofPars.otofLength}; // Radius and length std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; @@ -121,22 +122,23 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } else { LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } + if (itof) { // iTOF const std::string name = GeometryTGeo::getITOFLayerPattern(); - const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case - const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm - const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees - const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + const int nStaves = itofSegmented ? iotofPars.itofNStaves : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? iotofPars.itofStaveTilt : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 7 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, - dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, + dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? (iotofPars.itofTurbo ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrelStaggered) : ITOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave, itofSegmented ? sensorThickness : 0.0f); } if (otof) { // oTOF const std::string name = GeometryTGeo::getOTOFLayerPattern(); - const int nStaves = otofSegmented ? 62 : 0; // number of staves in segmented case - const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm - const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees - const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case + const int nStaves = otofSegmented ? iotofPars.otofNStaves : 0; // number of staves in segmented case + const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm + const double staveTiltAngle = otofSegmented ? iotofPars.otofStaveTilt : 0.0; // degrees + const int modulesPerStave = otofSegmented ? 38 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, otofSegmented ? OTOFLayer::kBarrelSegmented : OTOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave, otofSegmented ? sensorThickness : 0.0f); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h index 651174de8db5c..7b4b5c6d8f251 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h @@ -15,6 +15,7 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::iotof::TF3Chip + ; #pragma link C++ class o2::iotof::Layer + ; #pragma link C++ class o2::iotof::ITOFLayer + ; #pragma link C++ class o2::iotof::OTOFLayer + ; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 13b345fad51ee..3747567ebb4d3 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -11,6 +11,7 @@ #include "IOTOFSimulation/Layer.h" #include "IOTOFBase/GeometryTGeo.h" +#include "IOTOFSimulation/TF3Chip.h" #include "Framework/Logger.h" @@ -27,6 +28,18 @@ namespace o2 { namespace iotof { + +const int Layer::sChipsPerRow = 2; +const int Layer::sChipsPerCol = 6; +const double Layer::sChipGap = 200.e-4; // cm +const double Layer::sModuleGap = 200.e-4; // cm +const double Layer::sChipThickness = 300.e-4; // cm + +const double ITOFLayer::sStaggerRadialShift = 0.4; // cm + +const double OTOFLayer::sSubStavesDistanceY = 0.3; // cm +const double OTOFLayer::sSubStavesOverlapX = 1.1; // cm + Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave, float sensorThickness) : mLayerName(layerName), @@ -42,18 +55,20 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float mTiltAngle(staveTiltAngle) { const float Si_X0 = 9.5f; // cm, radiation length of silicon - mChipThickness = mX2X0 * Si_X0; + mLayerThickness = mX2X0 * Si_X0; + const bool isSegmented = (layout == kBarrelSegmented || layout == kDiskSegmented || layout == kBarrelStaggered); std::string name = ""; switch (layout) { case kBarrel: case kBarrelSegmented: + case kBarrelStaggered: name = "barrel"; - mOuterRadius = mInnerRadius + mChipThickness; + mOuterRadius = mInnerRadius + mLayerThickness; break; case kDisk: case kDiskSegmented: name = "forward"; - mZLength = mChipThickness; + mZLength = mLayerThickness; break; default: LOG(fatal) << "Invalid layout " << layout; @@ -62,23 +77,30 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float if (mInnerRadius > mOuterRadius) { LOG(fatal) << "Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm"; } - if ((mStaves.first != 0 || mStaves.second != 0.0f) && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + if ((mStaves.first != 0 || mStaves.second != 0.0f) && !isSegmented) { LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " is set for non-segmented layout " << layout; } - if ((mStaves.first <= 1 || mStaves.second <= 0.0f) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + if ((mStaves.first <= 1 || mStaves.second <= 0.0f) && isSegmented) { LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " must be positive for segmented layout " << layout; } - if (mModulesPerStave <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + if (mModulesPerStave <= 0 && isSegmented) { LOG(fatal) << "Invalid configuration: number of sensors per segment " << mModulesPerStave << " must be positive for segmented layout " << layout; } - if (std::abs(mTiltAngle) > 0.1 && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + if (std::abs(mTiltAngle) > 0.1 && !isSegmented) { LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is set for non-segmented layout " << layout; } - if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && isSegmented) { LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees"; } - if (mSensorThickness < 0.0f || mSensorThickness > mChipThickness) { - LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is out of range (0, " << mChipThickness << ") cm"; + // Staggered and tilted ("turbo") layouts are alternatives + if (layout == kBarrelStaggered && std::abs(mTiltAngle) > 1.e-6) { + LOG(fatal) << "Invalid configuration: staggered layout requires tilt angle = 0, got " << mTiltAngle; + } + if (layout == kBarrelStaggered && (mStaves.first % 2) != 0) { + LOG(fatal) << "Invalid configuration: staggered layout requires an even number of staves, got " << mStaves.first; + } + if (mSensorThickness < 0.0f || mSensorThickness > mLayerThickness) { + LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is out of range (0, " << mLayerThickness << ") cm"; } if (sensorThickness > 0.0f && (layout == kBarrel || layout == kDisk)) { LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is set for non-segmented layout, it should be 0"; @@ -99,7 +121,14 @@ void setStaveStyle(TGeoVolume* obj) obj->SetLineColor(kRed - 5); obj->SetFillColor(kRed - 9); obj->SetLineWidth(2); - obj->SetTransparency(45); + obj->SetTransparency(5); +} +void setOTOFStaveStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 5); + obj->SetFillColor(kRed - 9); + obj->SetLineWidth(2); + obj->SetTransparency(50); } void setModuleStyle(TGeoVolume* obj) { @@ -159,108 +188,96 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) motherVolume->AddNode(layerVol, 1, nullptr); return; } - case kBarrelSegmented: { + case kBarrelSegmented: + case kBarrelStaggered: { + // kBarrelSegmented: staves tilted by mTiltAngle, all at the same radius ("turbo" layout) + // kBarrelStaggered: untilted staves alternating between an inner and an outer radius + const bool staggered = (mLayout == kBarrelStaggered); + const double halfShift = staggered ? 0.5 * sStaggerRadialShift : 0.; // cm + // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm - // Build the mother layer tube from the exact inscribed/outscribed radii of a tilted stave rectangle. + // Build the mother layer tube from the exact inscribed/circumscribed radii of the tilted stave rectangle. + // For the staggered layout both the inner and the outer stave radii are taken into account. const double alpha = mTiltAngle * TMath::DegToRad(); - const double u0 = -avgRadius * std::cos(alpha); - const double v0 = avgRadius * std::sin(alpha); - const double uClamped = std::max(-0.5 * staveSizeY, std::min(0.5 * staveSizeY, u0)); - const double vClamped = std::max(-0.5 * staveSizeX, std::min(0.5 * staveSizeX, v0)); - const double radiusMin = std::hypot(uClamped - u0, vClamped - v0); - const double uCorners[4] = {-0.5 * staveSizeY, 0.5 * staveSizeY, 0.5 * staveSizeY, -0.5 * staveSizeY}; const double vCorners[4] = {-0.5 * staveSizeX, -0.5 * staveSizeX, 0.5 * staveSizeX, 0.5 * staveSizeX}; + double radiusMin = 1.e10; double radiusMax = 0.0; - for (int i = 0; i < 4; ++i) { - radiusMax = std::max(radiusMax, std::hypot(uCorners[i] - u0, vCorners[i] - v0)); + for (const int s : {-1, 1}) { + const double rStave = avgRadius + s * halfShift; + const double u0 = -rStave * std::cos(alpha); + const double v0 = rStave * std::sin(alpha); + const double uClamped = std::max(-0.5 * staveSizeY, std::min(0.5 * staveSizeY, u0)); + const double vClamped = std::max(-0.5 * staveSizeX, std::min(0.5 * staveSizeX, v0)); + radiusMin = std::min(radiusMin, std::hypot(uClamped - u0, vClamped - v0)); + for (int i = 0; i < 4; ++i) { + radiusMax = std::max(radiusMax, std::hypot(uCorners[i] - u0, vCorners[i] - v0)); + } } - TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); // cm, small margins to ensure staves are fully encapsulated in the layer volume + TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); // cm, encapsulates all staves TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); - // Now we create the volume for a single stave - TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); - TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); - setStaveStyle(staveVol); + // Now we create the volume for a single chip (chip + sensor) + TGeoVolume* chipVol = TF3Chip::createChip(sChipThickness, mSensorThickness, chipName, sensName); + ITOFLayer::mRegister.push_back(sensName); - // Now we create the volume for a single module (sensor + chip) - const int modulesPerStaveX = 1; // we assume that each stave is divided in 1 modules along the x direction - const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm - const double moduleSizeY = staveSizeY; // cm - const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm - TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); + // Now we create the volume for a single module + const double moduleSizeX = sChipsPerRow * TF3Chip::sChipSizeX + (sChipsPerRow - 1) * sChipGap; // cm + const double moduleSizeY = sChipThickness; // cm + const double moduleSizeZ = sChipsPerCol * TF3Chip::sChipSizeZ + (sChipsPerCol - 1) * sChipGap; // cm + TGeoBBox* module = new TGeoBBox(0.5 * moduleSizeX, moduleSizeY * 0.5, moduleSizeZ * 0.5); TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); setModuleStyle(moduleVol); - // Now we create the volume of the chip, which is the same for all modules - const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction - const int chipsPerModuleZ = 4; // we assume that each module is divided in 2 chips along the z direction - const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm - const double chipSizeY = moduleSizeY; // cm - const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm - TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); - TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); - setChipStyle(chipVol); - - // Finally we create the volume of the sensor, which is the same for all chips - const int sensorsPerChipX = 1; // we assume that each chip is divided in 2 sensors along the x direction - const int sensorsPerChipZ = 1; // we assume that each chip is divided in 2 sensors along the z direction - const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = mSensorThickness; // cm - const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm - TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); - TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); - setSensorStyle(sensVol); - ITOFLayer::mRegister.push_back(sensVol->GetName()); - - // Now we build a chip from sensors - for (int i = 0; i < sensorsPerChipX; ++i) { - for (int j = 0; j < sensorsPerChipZ; ++j) { - LOGP(info, "iTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); - auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, - 0.5 * chipSizeY - 0.5 * sensorSizeY, - (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); - chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); - } - } + // Now we create the volume for a single stave + TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medSi); + setStaveStyle(staveVol); // Now we build a module from chips - for (int i = 0; i < chipsPerModuleX; ++i) { - for (int j = 0; j < chipsPerModuleZ; ++j) { - LOGP(info, "iTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); - auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); - moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + for (int i = 0; i < sChipsPerRow; ++i) { + for (int j = 0; j < sChipsPerCol; ++j) { + LOGP(info, "iTOF: Creating chip {}/{} for module {}/{}", i + 1, sChipsPerRow, j + 1, sChipsPerCol); + auto* translation = new TGeoTranslation(Form("segmentTransChip%d", i + 1), (i - 0.5) * (TF3Chip::sChipSizeX + sChipGap), 0, (j + 0.5) * TF3Chip::sChipSizeZ + j * sChipGap - 0.5 * moduleSizeZ); + TGeoRotation* rotation = nullptr; + if (i == 1) { + rotation = new TGeoRotation(Form("segmentRotChip%d", i + 1), 90, 180, 90, 90, 180, 0); + } else { + rotation = new TGeoRotation(Form("segmentRotChip%d", i + 1), 0, 0, 0); + } + auto* transformation = new TGeoCombiTrans(*translation, *rotation); + moduleVol->AddNode(chipVol, 1 + i * sChipsPerCol + j, transformation); } } // Now we build a stave from modules - for (int i = 0; i < modulesPerStaveX; ++i) { - for (int j = 0; j < mModulesPerStave; ++j) { - LOGP(info, "iTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave); - auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ); - staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation); - } + for (int j = 0; j < mModulesPerStave; ++j) { + LOGP(info, "iTOF: Creating module for stave {}/{}", j + 1, mModulesPerStave); + auto* translation = new TGeoTranslation(0., staveSizeY * 0.5, (j + 0.5) * moduleSizeZ + j * 0.02 - 0.5 * staveSizeZ); + staveVol->AddNode(moduleVol, 1 + j, translation); } // We finally put all the staves in the layer for (int i = 0; i < mStaves.first; ++i) { LOGP(info, "iTOF: Creating stave {}/{} for layer {}", i + 1, mStaves.first, layerVol->GetName()); const double phi = TMath::TwoPi() * i / mStaves.first; - const double x = avgRadius * TMath::Cos(phi); - const double y = avgRadius * TMath::Sin(phi); + // staggered: even staves on the inner radius, odd staves on the outer radius + const double rStave = avgRadius + (staggered ? ((i % 2 == 0) ? -halfShift : halfShift) : 0.); + const double x = rStave * TMath::Cos(phi); + const double y = rStave * TMath::Sin(phi); auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + 90 + mTiltAngle, 0, 0); auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); - LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + LOGP(info, "Inserting Barrel {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, 1 + i, transformation); } - LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + LOGP(info, "Inserting Barrel {} in {} at r={} cm{}", layerVol->GetName(), motherVolume->GetName(), avgRadius, staggered ? " (staggered)" : ""); motherVolume->AddNode(layerVol, 1, nullptr); return; } @@ -307,18 +324,15 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) return; } case kBarrelSegmented: { - // Additional geometry parameters - const double subStavesDistanceY = 0.3; // cm - const double subStavesOverlapX = 1.1; // cm // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm, tangential stave size - const double staveSizeY = mOuterRadius - mInnerRadius + subStavesDistanceY; // cm, radial stave size - const double staveSizeZ = mZLength; // cm - const double subStaveSizeX = 0.5 * mStaves.second + 0.5 * subStavesOverlapX; // cm, tangential substave size - const double subStaveSizeY = mOuterRadius - mInnerRadius; // cm, radial substave size - const double subStaveSizeZ = mZLength; // cm + const double staveSizeX = mStaves.second; // cm, tangential stave size + const double staveSizeY = mOuterRadius - mInnerRadius + sSubStavesDistanceY; // cm, radial stave size + const double staveSizeZ = mZLength; // cm + const double subStaveSizeX = 0.5 * mStaves.second + 0.5 * sSubStavesOverlapX; // cm, tangential substave size + const double subStaveSizeY = mOuterRadius - mInnerRadius; // cm, radial substave size + const double subStaveSizeZ = mZLength; // cm // Build the mother layer tube from the exact inscribed/outscribed radii of a tilted stave rectangle. const double alpha = mTiltAngle * TMath::DegToRad(); @@ -338,89 +352,57 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); + // Now we create the volume for a single chip (chip + sensor) + TGeoVolume* chipVol = TF3Chip::createChip(sChipThickness, mSensorThickness, chipName, sensName); + OTOFLayer::mRegister.push_back(sensName); + + // Now we create the volume for a single module + const double moduleSizeX = sChipsPerRow * TF3Chip::sChipSizeX + (sChipsPerRow - 1) * sChipGap; // cm + const double moduleSizeY = sChipThickness; // cm + const double moduleSizeZ = sChipsPerCol * TF3Chip::sChipSizeZ + (sChipsPerCol - 1) * sChipGap; // cm + TGeoBBox* module = new TGeoBBox(0.5 * moduleSizeX, moduleSizeY * 0.5, moduleSizeZ * 0.5); + TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); + setModuleStyle(moduleVol); + // Now we create the volume for a single stave TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); - setStaveStyle(staveVol); + setOTOFStaveStyle(staveVol); - // Now we create the volume for a single stave + // Now we create the volume for a single substave + const int modulesPerSubStave = mModulesPerStave; TGeoBBox* subStave = new TGeoBBox(subStaveSizeX * 0.5, subStaveSizeY * 0.5, subStaveSizeZ * 0.5); - TGeoVolume* subStaveVol = new TGeoVolume(subStaveName, subStave, medAir); + TGeoVolume* subStaveVol = new TGeoVolume(subStaveName, subStave, medSi); setStaveStyle(subStaveVol); - // Now we create the volume for a single module (sensor + chip) - // oTOF V2 is a 2xN matrix. - const int modulesPerSubStave = mModulesPerStave; - const int modulesPerSubStaveX = 1; - if (modulesPerSubStave % modulesPerSubStaveX != 0) { - LOG(fatal) << "Invalid oTOF module layout: total modules per stave " << modulesPerSubStave - << " is not divisible by modulesPerStaveX=" << modulesPerSubStaveX; - } - const int modulesPerSubStaveZ = modulesPerSubStave / modulesPerSubStaveX; - const double moduleSizeX = subStaveSizeX / modulesPerSubStaveX; - const double moduleSizeY = subStaveSizeY; - const double moduleSizeZ = subStaveSizeZ / modulesPerSubStaveZ; - TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); - TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); - setModuleStyle(moduleVol); - - // Now we create the volume of the chip, which is the same for all modules - const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction - const int chipsPerModuleZ = 4; // we assume that each module is divided in 2 chips along the z direction - const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm - const double chipSizeY = moduleSizeY; // cm - const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm - TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); - TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); - setChipStyle(chipVol); - - // Finally we create the volume of the sensor, which is the same for all chips - const int sensorsPerChipX = 1; // we assume that each chip is divided in 2 sensors along the x direction - const int sensorsPerChipZ = 1; // we assume that each chip is divided in 2 sensors along the z direction - const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = mSensorThickness; // cm - const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm - TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); - TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); - setSensorStyle(sensVol); - OTOFLayer::mRegister.push_back(sensVol->GetName()); - - // Now we build a chip from sensors - for (int i = 0; i < sensorsPerChipX; ++i) { - for (int j = 0; j < sensorsPerChipZ; ++j) { - LOGP(info, "oTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); - auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, - 0.5 * chipSizeY - 0.5 * sensorSizeY, - (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); - chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); - } - } - // Now we build a module from chips - for (int i = 0; i < chipsPerModuleX; ++i) { - for (int j = 0; j < chipsPerModuleZ; ++j) { - LOGP(info, "oTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); - auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); - moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + for (int i = 0; i < sChipsPerRow; ++i) { + for (int j = 0; j < sChipsPerCol; ++j) { + LOGP(info, "oTOF: Creating chip {}/{} for module {}/{}", i + 1, sChipsPerRow, j + 1, sChipsPerCol); + auto* translation = new TGeoTranslation(Form("segmentTransChip%d", i + 1), (i - 0.5) * (TF3Chip::sChipSizeX + sChipGap), 0, (j + 0.5) * TF3Chip::sChipSizeZ + j * sChipGap - 0.5 * moduleSizeZ); + TGeoRotation* rotation = nullptr; + if (i == 1) { + rotation = new TGeoRotation(Form("segmentRotChip%d", i + 1), 90, 180, 90, 90, 180, 0); + } else { + rotation = new TGeoRotation(Form("segmentRotChip%d", i + 1), 0, 0, 0); + } + auto* transformation = new TGeoCombiTrans(*translation, *rotation); + moduleVol->AddNode(chipVol, 1 + i * sChipsPerCol + j, transformation); } } - // Now we build a sub-stave from modules - for (int i = 0; i < modulesPerSubStaveX; ++i) { - for (int j = 0; j < modulesPerSubStaveZ; ++j) { - LOGP(info, "oTOF: Creating module {}/{} for substave {}/{}", i + 1, modulesPerSubStaveX, j + 1, modulesPerSubStaveZ); - const double tx = (i + 0.5) * moduleSizeX - 0.5 * subStaveSizeX; - const double tz = -0.5 * subStaveSizeZ + (j + 0.5) * moduleSizeZ; - auto* translation = new TGeoTranslation(tx, 0, tz); - subStaveVol->AddNode(moduleVol, 1 + i * modulesPerSubStaveZ + j, translation); - } + // Now we build a substave from modules + for (int j = 0; j < modulesPerSubStave; ++j) { + LOGP(info, "oTOF: Creating module for substave {}/{}", j + 1, modulesPerSubStave); + auto* translation = new TGeoTranslation(0., subStaveSizeY * 0.5, (j + 0.5) * moduleSizeZ + j * 0.02 - 0.5 * subStaveSizeZ); + subStaveVol->AddNode(moduleVol, 1 + j, translation); } // Now we build a stave from two substave for (int i = 0; i < 2; ++i) { LOGP(info, "oTOF: Creating substave {}/{} for stave {}/{}", i + 1, 2, 1, 1); int sign = i > 0 ? 1 : -1; - auto* translation2 = new TGeoTranslation(sign * 0.5 * (subStaveSizeX)-sign * 0.5 * subStavesOverlapX, -sign * 0.5 * subStavesDistanceY, 0); + auto* translation2 = new TGeoTranslation(sign * 0.5 * subStaveSizeX - sign * 0.5 * sSubStavesOverlapX, -sign * 0.5 * sSubStavesDistanceY - 0.5 * moduleSizeY, 0); staveVol->AddNode(subStaveVol, i + 1, translation2); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/TF3Chip.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/TF3Chip.cxx new file mode 100644 index 0000000000000..91db1d0ac9bba --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/TF3Chip.cxx @@ -0,0 +1,145 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "IOTOFSimulation/TF3Chip.h" + +#include "DetectorsBase/MaterialManager.h" +#include "DetectorsBase/Detector.h" + +#include // for TGeoBBox +#include // for gGeoManager, TGeoManager +#include "TGeoVolume.h" // for TGeoVolume +#include "TGeoMatrix.h" // for TGeoMatrix +#include // for LOG + +namespace o2 +{ + +namespace iotof +{ + +const double TF3Chip::sChipSizeX = 2.6; // cm +const double TF3Chip::sChipSizeZ = 3.0; // cm + +//________________________________________________________________________ +TGeoVolume* TF3Chip::createChip(const Double_t ychip, + const Double_t ysens, + const char* chipName, + const char* sensName, + const Bool_t dummy, + const TGeoManager* mgr) +{ + + Double_t xchip, zchip; + Double_t ylen, ypos; + + xchip = 0.5 * sChipSizeX; + zchip = 0.5 * sChipSizeZ; + + // First create all needed shapes + ylen = ysens; + if (ysens > ychip) { + LOG(warning) << "Sensor half thickness (" << ysens + << ") greater than chip half thickness (" << ychip + << "), setting equal"; + ylen = ychip; + } + + // The chip + TGeoBBox* chip = new TGeoBBox(xchip, 0.5 * ychip, zchip); + + // The sensor + TGeoBBox* sensor = new TGeoBBox(xchip, 0.5 * ylen, zchip); + + // We have all shapes: now create the real volumes + TGeoMedium* medSi = mgr->GetMedium("TF3CHIP_SI$"); + if (!medSi) { + Int_t fieldType; + Float_t maxField; + o2::base::Detector::initFieldTrackingParams(fieldType, maxField); + createMaterials(0, fieldType, maxField); + medSi = mgr->GetMedium("TF3CHIP_SI$"); + } + TGeoMedium* medAir = mgr->GetMedium("TF3CHIP_AIR$"); + + TGeoMedium* medChip; + + if (dummy) { + medChip = medAir; + } else { + medChip = medSi; + } + + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medChip); + chipVol->SetVisibility(kTRUE); + chipVol->SetLineColor(kOrange); + chipVol->SetFillColor(kOrange - 9); + chipVol->SetLineWidth(3); + chipVol->SetTransparency(20); + + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medChip); + sensVol->SetVisibility(kTRUE); + sensVol->SetLineColor(kRed); + sensVol->SetLineWidth(3); + sensVol->SetFillColor(kRed - 9); + sensVol->SetFillStyle(4000); // 0% transparent + + // Now build up the chip + ypos = chip->GetDY() - sensor->GetDY(); + chipVol->AddNode(sensVol, 1, new TGeoTranslation(0., ypos, 0.)); + + // Done, return the chip + return chipVol; +} + +//________________________________________________________________________ +void TF3Chip::createMaterials(Int_t id, Int_t ifield, Float_t fieldm) +{ + auto& mgr = o2::base::MaterialManager::Instance(); + + Float_t tmaxfd = 0.1; // Degree + Float_t stemax = 1.0; // cm + Float_t deemax = 0.1; // Fraction of particle's energy 0