From 4c8b30d11e57ec0af26d2435c3a42c60eab7e6f1 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sat, 29 Aug 2026 15:27:54 +0200 Subject: [PATCH 1/3] Position the TOF cooling cross members without MANY The twelve FTLN placements were made with the GEANT3 MANY flag, which marks a node as overlapping its siblings. They do not overlap anything. - Sampling points inside every MANY-placed TOF node and testing them against each sibling finds no sibling containing any point of an FTLN copy. - With ONLY, TGeo's overlap checker examines these placements for the first time and reports no overlap in FAIA, FAIB or FAIC. - Declared overlapping nodes in the geometry drop from 3215 to 3071; the material is unchanged, since the flag was not resolving anything. This removes twelve of the sixty-six MANY placements. The remainder are real overlaps and need the shapes to be changed. --- Detectors/TOF/simulation/src/Detector.cxx | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index 97d5e03851291..4ad12ad3fa5f8 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -1370,14 +1370,14 @@ void Detector::makeSuperModuleCooling(Float_t xtof, Float_t ytof, Float_t zlenA) Float_t yFLTN = trapar[1] - (ytof * 0.5 - Geo::MODULECOVERTHICKNESS) * 0.5; for (Int_t sg = -1; sg < 2; sg += 2) { // Positioning of transverse components for the SM cooling system - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIA", 0., yFLTN, 369.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIA", 0., yFLTN, 366.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIA", 0., yFLTN, 198.8 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIA", 0., yFLTN, 56.82 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIC", 0., yFLTN, 369.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIC", 0., yFLTN, 366.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIC", 0., yFLTN, 198.8 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIC", 0., yFLTN, 56.82 * sg, 0, "MANY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIA", 0., yFLTN, 369.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIA", 0., yFLTN, 366.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIA", 0., yFLTN, 198.8 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIA", 0., yFLTN, 56.82 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIC", 0., yFLTN, 369.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIC", 0., yFLTN, 366.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIC", 0., yFLTN, 198.8 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIC", 0., yFLTN, 56.82 * sg, 0, "ONLY"); } // definition of longitudinal components of SM cooling system @@ -1446,10 +1446,10 @@ void Detector::makeSuperModuleCooling(Float_t xtof, Float_t ytof, Float_t zlenA) if (mTOFHoles) { for (Int_t sg = -1; sg < 2; sg += 2) { carpos[2] = sg * zlenA * 0.5; - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIB", 0., yFLTN, 369.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIB", 0., yFLTN, 366.9 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIB", 0., yFLTN, 198.8 * sg, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIB", 0., yFLTN, 56.82 * sg, 0, "MANY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 4 * sg, "FAIB", 0., yFLTN, 369.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 3 * sg, "FAIB", 0., yFLTN, 366.9 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + 2 * sg, "FAIB", 0., yFLTN, 198.8 * sg, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIB", 0., yFLTN, 56.82 * sg, 0, "ONLY"); } ycoor = ytub + (tubepar[1] + 2. * bar2[1] + lonpar1[1]); From 4a34971f9da424e7efdda68e25e95f90cf5fd0fb Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sat, 29 Aug 2026 16:58:51 +0200 Subject: [PATCH 2/3] Seat the TOF Nino mask in a groove instead of a MANY overlap The FRO1 FEA roof was a box with a FREE air daughter cut into its top face, and the FRO2 Nino mask was placed across that groove as a sibling, so FRO1 had to be declared overlapping. FRO1 is now a box with the groove subtracted from the shape, FREE is gone, and both are placed ONLY. - The subtracted groove is the old FREE box, oversized by 10 um where it leaves the parent box so the two solids share no face. - The groove is now the crate's own air, which is the same medium FREE was. - Declared overlapping nodes in the geometry drop from 3071 to 339, and node objects from 5465306 to 5458934. TGeo's overlap checker looks at FCA1 for the first time and reports no overlap. - The material is unchanged. A material budget LUT over R = 351-431 cm, 1340840 cells, agrees before and after to 1e-7 in all but two cells, which differ by 0.14%. - Reaching that comparison needed a fix in TGeo. TGeo consults a MANY node's overlap list only while that node is the current one, so FRO1's FREE daughter hid FRO2 from the stepping although FindNode() reported it at the same points. Against the unfixed navigator the same comparison gives 4525 differing cells and up to 14% more material, all of it an artefact of that defect. Co-Authored-By: Claude Opus 5 --- Detectors/TOF/prototyping/drawTOFgeometry.C | 4 +-- Detectors/TOF/simulation/src/Detector.cxx | 32 +++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Detectors/TOF/prototyping/drawTOFgeometry.C b/Detectors/TOF/prototyping/drawTOFgeometry.C index 49232cf741b5a..740f53ff7adc9 100644 --- a/Detectors/TOF/prototyping/drawTOFgeometry.C +++ b/Detectors/TOF/prototyping/drawTOFgeometry.C @@ -70,7 +70,7 @@ void drawTOFgeometry() "BFRB BFRR BBMO BBCE BBTRD BBLB BBLL BBRB BBRR BBC1 BBC2 BBC3 BBC4 BBD1 BBD3 BBD2 BBD4 FTOA FTOB FTOC FLTA FLTB " "FLTC FWZ1D FWZAD FWZ1U FWZBU FWZ2 FWZC FWZ3 FWZ4 FSTR FHON FPC1 FPC2 FPCB FSEN FSEZ FPAD FRGL FGLF FPEA FPEB " "FALT FALB FPE1 FPE4 FPE2 FPE3 FIF1 FIF2 FIF3 FFC1 FFC2 FFC3 FCC1 FCC2 FCC3 FAIA FAIB FAIC FCA1 FCA2 FFEA FAL1 " - "FRO1 FREE FBAR FBA1 FBA2 FAL2 FAL3 FRO2 FTUB FITU FTLN FLO1 FLO2 FLO3 FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB " + "FRO1 FBAR FBA1 FBA2 FAL2 FAL3 FRO2 FTUB FITU FTLN FLO1 FLO2 FLO3 FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB " "FCOV FCOB FCOP FTOS"; TObjArray* lToHide = ToHide.Tokenize(" "); @@ -82,7 +82,7 @@ void drawTOFgeometry() TString ToShow = "BTOF0 BFMO BFIR BFOR BFLB BFRB BBMO BBCE BBLB BBRB FTOA FTOB FTOC FLTA FLTB FLTC FWZ1D FWZAD FWZ1U FWZBU FWZ2 " "FWZC FWZ3 FWZ4 FSTR FHON FPC1 FPC2 FPCB FSEN FSEZ FPAD FRGL FGLF FPEA FPEB FALT FALB FPE1 FPE4 FPE2 FPE3 FIF1 " - "FIF2 FIF3 FFC1 FFC2 FFC3 FCC1 FCC2 FCC3 FAIA FAIB FAIC FCA1 FCA2 FFEA FAL1 FRO1 FREE FBAR FBA1 FBA2 FAL2 FAL3 " + "FIF2 FIF3 FFC1 FFC2 FFC3 FCC1 FCC2 FCC3 FAIA FAIB FAIC FCA1 FCA2 FFEA FAL1 FRO1 FBAR FBA1 FBA2 FAL2 FAL3 " "FRO2 FTUB FITU FTLN FLO1 FLO2 FLO3 FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB FCOV FCOB FCOP FTOS"; // ToShow.ReplaceAll("FCOV", "");//Remove external cover but PHOS hole // ToShow.ReplaceAll("FLTA", "");//Remove internal cover but PHOS hole diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index 4ad12ad3fa5f8..aac306c4e7826 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -9,7 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "TGeoBBox.h" +#include "TGeoCompositeShape.h" #include "TGeoManager.h" // for TGeoManager +#include "TGeoMatrix.h" +#include "TGeoVolume.h" #include "TMath.h" #include "TString.h" @@ -21,6 +25,7 @@ #include // for TVirtualMC, gMC #include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/MaterialManager.h" #include "DetectorsBase/Stack.h" using namespace o2::tof; @@ -1144,19 +1149,22 @@ void Detector::makeFEACooling(Float_t xtof) const Float_t al1[3] = {Geo::AL1PARAMETERS[0], Geo::AL1PARAMETERS[1], Geo::AL1PARAMETERS[2]}; TVirtualMC::GetMC()->Gsvolu("FAL1", "BOX ", getMediumID(kAlFrame), al1, 3); // Al - // second FEA cooling element definition + // second FEA cooling element definition: an Al roof with the FRO2 Nino-mask groove cut out of + // its shape. The groove is oversized by kGrooveEps where it leaves the box, so that the two + // solids share no face. Float_t feaRoof1[3] = {Geo::ROOF1PARAMETERS[0], Geo::ROOF1PARAMETERS[1], Geo::ROOF1PARAMETERS[2]}; - TVirtualMC::GetMC()->Gsvolu("FRO1", "BOX ", getMediumID(kAlFrame), feaRoof1, 3); // Al + Float_t airHole[3] = {Geo::ROOF2PARAMETERS[0], static_cast(Geo::ROOF2PARAMETERS[1] * 0.5), feaRoof1[2]}; + const Double_t kGrooveEps = 1.e-3; // cm + new TGeoBBox("FRO1box", feaRoof1[0], feaRoof1[1], feaRoof1[2]); + new TGeoBBox("FRO1groove", airHole[0], airHole[1] + kGrooveEps, airHole[2] + kGrooveEps); + auto* fro1GrooveTr = new TGeoTranslation("FRO1grooveTr", 0., feaRoof1[1] - airHole[1] + kGrooveEps, 0.); + fro1GrooveTr->RegisterYourself(); + auto* fro1Shape = new TGeoCompositeShape("FRO1shape", "FRO1box-(FRO1groove:FRO1grooveTr)"); + new TGeoVolume("FRO1", fro1Shape, o2::base::MaterialManager::Instance().getTGeoMedium(GetName(), kAlFrame)); // Al Float_t al3[3] = {Geo::AL3PARAMETERS[0], Geo::AL3PARAMETERS[1], Geo::AL3PARAMETERS[2]}; // Float_t feaRoof2[3] = {Geo::ROOF2PARAMETERS[0], Geo::ROOF2PARAMETERS[1], Geo::ROOF2PARAMETERS[2]}; - // definition and positioning of a small air groove in the FRO1 volume - Float_t airHole[3] = {Geo::ROOF2PARAMETERS[0], static_cast(Geo::ROOF2PARAMETERS[1] * 0.5), feaRoof1[2]}; - TVirtualMC::GetMC()->Gsvolu("FREE", "BOX ", getMediumID(kAir), airHole, 3); // Air - TVirtualMC::GetMC()->Gspos("FREE", 1, "FRO1", 0., feaRoof1[1] - airHole[1], 0., 0, "ONLY"); - gGeoManager->GetVolume("FRO1")->VisibleDaughters(kFALSE); - // third FEA cooling element definition Float_t bar[3] = {Geo::BAR[0], Geo::BAR[1], Geo::BAR[2]}; TVirtualMC::GetMC()->Gsvolu("FBAR", "BOX ", getMediumID(kAlFrame), bar, 3); // Al @@ -1193,13 +1201,13 @@ void Detector::makeFEACooling(Float_t xtof) const xcoor = xtof * 0.5 - 25.; ycoor = carpar[1] - 2. * Geo::ROOF2PARAMETERS[1] * 0.5 - feaRoof1[1]; zcoor = -carpar[2] + feaRoof1[2]; - TVirtualMC::GetMC()->Gspos("FRO1", 1, "FCA1", -xcoor, ycoor, zcoor, 0, "MANY"); // (AdC) - TVirtualMC::GetMC()->Gspos("FRO1", 4, "FCA1", xcoor, ycoor, zcoor, 0, "MANY"); // (AdC) + TVirtualMC::GetMC()->Gspos("FRO1", 1, "FCA1", -xcoor, ycoor, zcoor, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FRO1", 4, "FCA1", xcoor, ycoor, zcoor, 0, "ONLY"); TVirtualMC::GetMC()->Gspos("FRO1", 1, "FCA2", -xcoor, ycoor, zcoor, 0, "ONLY"); TVirtualMC::GetMC()->Gspos("FRO1", 4, "FCA2", xcoor, ycoor, zcoor, 0, "ONLY"); xcoor = feaParam[0] + (Geo::FEAWIDTH2 * 0.5 - Geo::FEAWIDTH1); - TVirtualMC::GetMC()->Gspos("FRO1", 2, "FCA1", -xcoor, ycoor, zcoor, 0, "MANY"); // (AdC) - TVirtualMC::GetMC()->Gspos("FRO1", 3, "FCA1", xcoor, ycoor, zcoor, 0, "MANY"); // (AdC) + TVirtualMC::GetMC()->Gspos("FRO1", 2, "FCA1", -xcoor, ycoor, zcoor, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FRO1", 3, "FCA1", xcoor, ycoor, zcoor, 0, "ONLY"); TVirtualMC::GetMC()->Gspos("FRO1", 2, "FCA2", -xcoor, ycoor, zcoor, 0, "ONLY"); TVirtualMC::GetMC()->Gspos("FRO1", 3, "FCA2", xcoor, ycoor, zcoor, 0, "ONLY"); From 6ed5338bd27c99db8defde7f17dbb6f11b2ed1a9 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sat, 29 Aug 2026 21:13:08 +0200 Subject: [PATCH 3/3] Place the TOF cooling bars between the FEA containers instead of through them This removes the last 339 MANY placements in the ALICE geometry and leaves the material unchanged. - The FLO1/FLO2/FLO3 longitudinal cooling bars were declared overlapping so that each could cross about nineteen FEA card containers. The containers are placed ONLY and take priority, so a bar already carried no material inside one. Each bar is now placed as the pieces that survive between them. - The one container per supermodule that was itself MANY is the exception: there the bar takes priority, not the container. It is now a TGeoVolumeAssembly of the same content, which has no shape of its own and so overlaps nothing, and the air around it is the medium its box carried. - The row table that puts the containers along z is now written once and read by both the container placement and the bar segmentation. - Declared overlapping nodes in the ALICE geometry fall from 339 to 0. Node objects rise from 5458934 to 5467718 and volumes from 6918 to 6933. - The material is unchanged. 1368000 points inside the bar placements and across the containers give the same medium in both geometries, and a density integral sampled along a 720 cm ray down a bar gives 1166.747000 g/cm2 in both. - TGeo's stepping did not agree with its own point location before. The same ray stepped 1158.110802 g/cm2, losing one container width of aluminium at the MANY container; it now steps 1166.746947. - A material budget LUT over R = 351-431 cm has 102 of 1340840 cells differing by more than 1e-5, 100 of them in the two z bins holding that container. The remaining two have identical material on a 216000-point grid. Co-Authored-By: Claude Opus 5 --- Detectors/TOF/prototyping/drawTOFgeometry.C | 11 +- .../include/TOFSimulation/Detector.h | 26 ++ Detectors/TOF/simulation/src/Detector.cxx | 307 +++++++++++------- 3 files changed, 220 insertions(+), 124 deletions(-) diff --git a/Detectors/TOF/prototyping/drawTOFgeometry.C b/Detectors/TOF/prototyping/drawTOFgeometry.C index 740f53ff7adc9..635f2962c579a 100644 --- a/Detectors/TOF/prototyping/drawTOFgeometry.C +++ b/Detectors/TOF/prototyping/drawTOFgeometry.C @@ -70,7 +70,7 @@ void drawTOFgeometry() "BFRB BFRR BBMO BBCE BBTRD BBLB BBLL BBRB BBRR BBC1 BBC2 BBC3 BBC4 BBD1 BBD3 BBD2 BBD4 FTOA FTOB FTOC FLTA FLTB " "FLTC FWZ1D FWZAD FWZ1U FWZBU FWZ2 FWZC FWZ3 FWZ4 FSTR FHON FPC1 FPC2 FPCB FSEN FSEZ FPAD FRGL FGLF FPEA FPEB " "FALT FALB FPE1 FPE4 FPE2 FPE3 FIF1 FIF2 FIF3 FFC1 FFC2 FFC3 FCC1 FCC2 FCC3 FAIA FAIB FAIC FCA1 FCA2 FFEA FAL1 " - "FRO1 FBAR FBA1 FBA2 FAL2 FAL3 FRO2 FTUB FITU FTLN FLO1 FLO2 FLO3 FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB " + "FRO1 FBAR FBA1 FBA2 FAL2 FAL3 FRO2 FTUB FITU FTLN FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB " "FCOV FCOB FCOP FTOS"; TObjArray* lToHide = ToHide.Tokenize(" "); @@ -83,7 +83,7 @@ void drawTOFgeometry() "BTOF0 BFMO BFIR BFOR BFLB BFRB BBMO BBCE BBLB BBRB FTOA FTOB FTOC FLTA FLTB FLTC FWZ1D FWZAD FWZ1U FWZBU FWZ2 " "FWZC FWZ3 FWZ4 FSTR FHON FPC1 FPC2 FPCB FSEN FSEZ FPAD FRGL FGLF FPEA FPEB FALT FALB FPE1 FPE4 FPE2 FPE3 FIF1 " "FIF2 FIF3 FFC1 FFC2 FFC3 FCC1 FCC2 FCC3 FAIA FAIB FAIC FCA1 FCA2 FFEA FAL1 FRO1 FBAR FBA1 FBA2 FAL2 FAL3 " - "FRO2 FTUB FITU FTLN FLO1 FLO2 FLO3 FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB FCOV FCOB FCOP FTOS"; + "FRO2 FTUB FITU FTLN FBAS FBS1 FBS2 FCAB FCAL FCBL FSAW FCBB FCOV FCOB FCOP FTOS"; // ToShow.ReplaceAll("FCOV", "");//Remove external cover but PHOS hole // ToShow.ReplaceAll("FLTA", "");//Remove internal cover but PHOS hole ToShow.ReplaceAll("FFC1", ""); // Remove internal cover but PHOS hole @@ -101,6 +101,13 @@ void drawTOFgeometry() while ((name = (TObjString*)iToShow->Next())) gGeoManager->GetVolume(name->GetName())->SetVisibility(kTRUE); + // the pieces of the SM longitudinal cooling bars, whose volumes are named at build time + TIter iVolume(gGeoManager->GetListOfVolumes()); + TGeoVolume* volume; + while ((volume = (TGeoVolume*)iVolume())) + if (TString(volume->GetName()).BeginsWith("FLOS")) + volume->SetVisibility(kTRUE); + const TString ToTrans = "FTOS FCOV FLTA"; TObjArray* lToTrans = ToTrans.Tokenize(" "); diff --git a/Detectors/TOF/simulation/include/TOFSimulation/Detector.h b/Detectors/TOF/simulation/include/TOFSimulation/Detector.h index 86f86acc61846..7ce944a1ed386 100644 --- a/Detectors/TOF/simulation/include/TOFSimulation/Detector.h +++ b/Detectors/TOF/simulation/include/TOFSimulation/Detector.h @@ -18,6 +18,12 @@ #include "SimulationDataFormat/BaseHits.h" #include "CommonUtils/ShmAllocator.h" +#include +#include +#include + +class TGeoVolume; + class FairVolume; namespace o2 @@ -114,6 +120,22 @@ class Detector : public o2::base::DetImpl void makeFEACooling(Float_t xtof) const; void makeNinoMask(Float_t xtof) const; void makeSuperModuleCooling(Float_t xtof, Float_t ytof, Float_t zlenA) const; + /// one FEA card container of a supermodule: where it sits along z and how it is placed + struct FEAContainer { + Float_t z; + Int_t row; + Bool_t rotated; + }; + /// returns the FEA card containers of one supermodule, in placement order; creates nothing + std::vector feaContainers(Float_t zlenA, Bool_t holes) const; + /// creates the FCM1/FCM2 assemblies, the central FEA card container, and places them in FAIA/FAIC + void makeCentralFEAContainer(Float_t ytof) const; + /// returns the volume for one piece of a cooling bar, creating it the first time a size is asked for + TGeoVolume* coolingBarPiece(Double_t dx, Double_t dy, Double_t dz) const; + /// places one longitudinal cooling bar as the pieces that survive between the FEA containers + void placeCoolingBar(const char* mother, const std::vector& cont, Double_t crateDZ, + Double_t crateY0, Double_t crateY1, Double_t xcoor, Double_t dx, Double_t ycoor, + Double_t dy, Double_t zcoor, Double_t dz, Int_t& copy) const; void makeSuperModuleServices(Float_t xtof, Float_t ytof, Float_t zlenA) const; void makeReadoutCrates(Float_t ytof) const; @@ -142,6 +164,10 @@ class Detector : public o2::base::DetImpl /// container for data points std::vector* mHits; //! + /// the cooling-bar piece volumes created so far, keyed by the piece half-sizes {dx, dy, dz}, + /// so that each distinct size is created once and placed many times + mutable std::map, TGeoVolume*> mBarPieces; //! + template friend class o2::base::DetImpl; ClassDefOverride(Detector, 1); diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index aac306c4e7826..ca3b51f2c3380 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -28,6 +28,12 @@ #include "DetectorsBase/MaterialManager.h" #include "DetectorsBase/Stack.h" +#include +#include +#include +#include +#include + using namespace o2::tof; ClassImp(Detector); @@ -340,6 +346,7 @@ void Detector::DefineGeometry(Float_t xtof, Float_t ytof, Float_t zlenA) makeNinoMask(xtof); makeSuperModuleCooling(xtof, ytof, zlenA); makeSuperModuleServices(xtof, ytof, zlenA); + makeCentralFEAContainer(ytof); makeModulesInBTOFvolumes(ytof, zlenA); makeCoversInBTOFvolumes(); @@ -1001,6 +1008,138 @@ void Detector::createModuleCovers(Float_t xtof, Float_t zlenA) const TVirtualMC::GetMC()->Gspos("FCC3", 0, "FFC3", 0., 0., 0., 0, "ONLY"); } +std::vector Detector::feaContainers(Float_t zlenA, Bool_t holes) const +{ + // + // Returns the FEA card containers of one supermodule in placement order, each with the z it + // sits at, its copy number and whether it is rotated. Creates and places nothing itself. The + // modules with the PHOS hole (holes) carry four row blocks instead of five. The container at + // the centre of the supermodule is not in the list: makeCentralFEAContainer builds that one. + // + + const Float_t rowstep = 6.66; + const Float_t rowgap[5] = {13.5, 22.9, 16.94, 23.8, 20.4}; + const Int_t rowb[5] = {6, 7, 6, 19, 7}; + const Int_t nblocks = holes ? 4 : 5; + + std::vector cont; + Int_t row = 1; + for (Int_t sg = -1; sg < 2; sg += 2) { + Float_t zcoor = sg * zlenA * 0.5 - 0.8; + for (Int_t nb = 0; nb < nblocks; ++nb) { + zcoor = zcoor - sg * (rowgap[nb] - rowstep); + const Int_t nrow = row + rowb[nb]; + for (; row < nrow; ++row) { + zcoor -= sg * rowstep; + cont.push_back({zcoor, row, sg == -1 && nb != 4}); + } + } + } + return cont; +} + +void Detector::makeCentralFEAContainer(Float_t ytof) const +{ + // + // Creates FCM1 and FCM2, the FEA card container at the centre of a supermodule, as assemblies + // of the FCA1/FCA2 content, and places one in FAIA and one in FAIC. Here it is the container + // that gives way to the cooling bars and not the other way round, and an assembly has no shape + // of its own to overlap them. What was its air is now the FAIA/FAIC air around it, which is the + // same medium. + // + + const Float_t carY = Geo::FEAPARAMETERS[1] + Geo::ROOF1PARAMETERS[1] + Geo::ROOF2PARAMETERS[1] * 0.5; + const Float_t ycoor = -(ytof * 0.5 - Geo::MODULECOVERTHICKNESS) * 0.5 + carY; + + const char* source[2] = {"FCA1", "FCA2"}; + const char* central[2] = {"FCM1", "FCM2"}; + const char* mother[2] = {"FAIA", "FAIC"}; + for (Int_t i = 0; i < 2; ++i) { + TGeoVolume* from = gGeoManager->GetVolume(source[i]); + auto* assembly = new TGeoVolumeAssembly(central[i]); + for (Int_t k = 0; k < from->GetNdaughters(); ++k) { + TGeoNode* nd = from->GetNode(k); + assembly->AddNode(nd->GetVolume(), nd->GetNumber(), new TGeoHMatrix(*nd->GetMatrix())); + } + gGeoManager->GetVolume(mother[i])->AddNode(assembly, 91, new TGeoTranslation(0., ycoor, -0.8)); + } +} + +TGeoVolume* Detector::coolingBarPiece(Double_t dx, Double_t dy, Double_t dz) const +{ + // + // Returns the volume for one piece of a segmented longitudinal cooling bar, creating it the + // first time that size is asked for. The pieces come in a handful of sizes that repeat all + // along a supermodule, so each size becomes one volume placed many times. The sizes compare + // exactly because every caller derives them from the same arithmetic. + // + + const std::array key{dx, dy, dz}; + auto it = mBarPieces.find(key); + if (it != mBarPieces.end()) { + return it->second; + } + + const TString name = TString::Format("FLOS%zu", mBarPieces.size() + 1); + auto* vol = new TGeoVolume(name, new TGeoBBox(name + "box", dx, dy, dz), + o2::base::MaterialManager::Instance().getTGeoMedium(GetName(), kAlFrame)); // Al + mBarPieces[key] = vol; + return vol; +} + +void Detector::placeCoolingBar(const char* mother, const std::vector& cont, Double_t crateDZ, + Double_t crateY0, Double_t crateY1, Double_t xcoor, Double_t dx, Double_t ycoor, + Double_t dy, Double_t zcoor, Double_t dz, Int_t& copy) const +{ + // + // Places one longitudinal cooling bar in mother, as the pieces that survive between the FEA + // card containers it crosses. A bar crosses about nineteen of them, and they are placed ONLY + // and so take priority over it. Advances copy past the pieces it places. + // + + const Double_t barZ0 = zcoor - dz, barZ1 = zcoor + dz; + const Double_t barY0 = ycoor - dy, barY1 = ycoor + dy; + + // the container slabs that really cut this bar, along z + std::vector> cut; + if (crateY1 > barY0 && crateY0 < barY1) { + for (auto const& c : cont) { + const Double_t z0 = std::max(barZ0, c.z - crateDZ); + const Double_t z1 = std::min(barZ1, c.z + crateDZ); + if (z1 > z0) { + cut.emplace_back(z0, z1); + } + } + std::sort(cut.begin(), cut.end()); + } + + // the bar at full height, in the gaps between containers + Double_t z = barZ0; + for (auto const& c : cut) { + if (c.first > z) { + TVirtualMC::GetMC()->Gspos(coolingBarPiece(dx, dy, 0.5 * (c.first - z))->GetName(), ++copy, mother, + xcoor, ycoor, 0.5 * (z + c.first), 0, "ONLY"); + } + z = std::max(z, c.second); + } + if (barZ1 > z) { + TVirtualMC::GetMC()->Gspos(coolingBarPiece(dx, dy, 0.5 * (barZ1 - z))->GetName(), ++copy, mother, + xcoor, ycoor, 0.5 * (z + barZ1), 0, "ONLY"); + } + + // and, where the bar is taller than the container it crosses, the strip that stands proud of it + const Double_t strip[2][2] = {{barY0, std::min(barY1, crateY0)}, {std::max(barY0, crateY1), barY1}}; + for (auto const& c : cut) { + for (auto const& sy : strip) { + if (sy[1] <= sy[0]) { + continue; + } + TVirtualMC::GetMC()->Gspos(coolingBarPiece(dx, 0.5 * (sy[1] - sy[0]), 0.5 * (c.second - c.first))->GetName(), + ++copy, mother, xcoor, 0.5 * (sy[0] + sy[1]), 0.5 * (c.first + c.second), 0, "ONLY"); + } + } +} + void Detector::createBackZone(Float_t xtof, Float_t ytof, Float_t zlenA) const { // @@ -1042,63 +1181,16 @@ void Detector::createBackZone(Float_t xtof, Float_t ytof, Float_t zlenA) const Matrix(idrotm[0], 90., 180., 90., 90., 180., 0.); // FEA card mother-volume positioning - Float_t rowstep = 6.66; - Float_t rowgap[5] = {13.5, 22.9, 16.94, 23.8, 20.4}; - Int_t rowb[5] = {6, 7, 6, 19, 7}; Float_t carpos[3] = {0., static_cast(-(ytof * 0.5 - Geo::MODULECOVERTHICKNESS) * 0.5 + carpar[1]), -0.8}; - TVirtualMC::GetMC()->Gspos("FCA1", 91, "FAIA", carpos[0], carpos[1], carpos[2], 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FCA2", 91, "FAIC", carpos[0], carpos[1], carpos[2], 0, "MANY"); - - Int_t row = 1; - Int_t nrow = 0; - for (Int_t sg = -1; sg < 2; sg += 2) { - carpos[2] = sg * zlenA * 0.5 - 0.8; - for (Int_t nb = 0; nb < 5; ++nb) { - carpos[2] = carpos[2] - sg * (rowgap[nb] - rowstep); - nrow = row + rowb[nb]; - for (; row < nrow; ++row) { - carpos[2] -= sg * rowstep; - - if (nb == 4) { - TVirtualMC::GetMC()->Gspos("FCA1", row, "FAIA", carpos[0], carpos[1], carpos[2], 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("FCA2", row, "FAIC", carpos[0], carpos[1], carpos[2], 0, "ONLY"); - } else { - switch (sg) { - case 1: - TVirtualMC::GetMC()->Gspos("FCA1", row, "FAIA", carpos[0], carpos[1], carpos[2], 0, "ONLY"); - TVirtualMC::GetMC()->Gspos("FCA2", row, "FAIC", carpos[0], carpos[1], carpos[2], 0, "ONLY"); - break; - case -1: - TVirtualMC::GetMC()->Gspos("FCA1", row, "FAIA", carpos[0], carpos[1], carpos[2], idrotm[0], "ONLY"); - TVirtualMC::GetMC()->Gspos("FCA2", row, "FAIC", carpos[0], carpos[1], carpos[2], idrotm[0], "ONLY"); - break; - } - } - } - } + for (auto const& c : feaContainers(zlenA, kFALSE)) { + TVirtualMC::GetMC()->Gspos("FCA1", c.row, "FAIA", carpos[0], carpos[1], c.z, c.rotated ? idrotm[0] : 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("FCA2", c.row, "FAIC", carpos[0], carpos[1], c.z, c.rotated ? idrotm[0] : 0, "ONLY"); } if (mTOFHoles) { - row = 1; - for (Int_t sg = -1; sg < 2; sg += 2) { - carpos[2] = sg * zlenA * 0.5 - 0.8; - for (Int_t nb = 0; nb < 4; ++nb) { - carpos[2] = carpos[2] - sg * (rowgap[nb] - rowstep); - nrow = row + rowb[nb]; - for (; row < nrow; ++row) { - carpos[2] -= sg * rowstep; - - switch (sg) { - case 1: - TVirtualMC::GetMC()->Gspos("FCA1", row, "FAIB", carpos[0], carpos[1], carpos[2], 0, "ONLY"); - break; - case -1: - TVirtualMC::GetMC()->Gspos("FCA1", row, "FAIB", carpos[0], carpos[1], carpos[2], idrotm[0], "ONLY"); - break; - } - } - } + for (auto const& c : feaContainers(zlenA, kTRUE)) { + TVirtualMC::GetMC()->Gspos("FCA1", c.row, "FAIB", carpos[0], carpos[1], c.z, c.rotated ? idrotm[0] : 0, "ONLY"); } } } @@ -1392,62 +1484,36 @@ void Detector::makeSuperModuleCooling(Float_t xtof, Float_t ytof, Float_t zlenA) Float_t lonpar1[3] = {2., 0.5, static_cast(56.82 - trapar[2])}; Float_t lonpar2[3] = {lonpar1[0], lonpar1[1], static_cast((198.8 - 56.82) * 0.5 - trapar[2])}; Float_t lonpar3[3] = {lonpar1[0], lonpar1[1], static_cast((366.9 - 198.8) * 0.5 - trapar[2])}; - TVirtualMC::GetMC()->Gsvolu("FLO1", "BOX ", getMediumID(kAlFrame), lonpar1, 3); // Al - TVirtualMC::GetMC()->Gsvolu("FLO2", "BOX ", getMediumID(kAlFrame), lonpar2, 3); // Al - TVirtualMC::GetMC()->Gsvolu("FLO3", "BOX ", getMediumID(kAlFrame), lonpar3, 3); // Al - - // Positioning of longitudinal components for the SM cooling system - ycoor = ytub + (tubepar[1] + 2. * bar2[1] + lonpar1[1]); - TVirtualMC::GetMC()->Gspos("FLO1", 4, "FAIA", -24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 2, "FAIA", 24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 4, "FAIC", -24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 2, "FAIC", 24., ycoor, 0., 0, "MANY"); - - zcoor = (198.8 + 56.82) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO2", 4, "FAIA", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 2, "FAIA", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 4, "FAIC", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 2, "FAIC", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 8, "FAIA", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 6, "FAIA", 24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 8, "FAIC", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 6, "FAIC", 24., ycoor, zcoor, 0, "MANY"); - - zcoor = (366.9 + 198.8) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO3", 4, "FAIA", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 2, "FAIA", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 4, "FAIC", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 2, "FAIC", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 8, "FAIA", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 6, "FAIA", 24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 8, "FAIC", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 6, "FAIC", 24., ycoor, zcoor, 0, "MANY"); - - ycoor = ytub - (tubepar[1] + 2. * bar2[1] + lonpar1[1]); - TVirtualMC::GetMC()->Gspos("FLO1", 3, "FAIA", -24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 1, "FAIA", 24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 3, "FAIC", -24., ycoor, 0., 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO1", 1, "FAIC", 24., ycoor, 0., 0, "MANY"); - - zcoor = (198.8 + 56.82) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO2", 3, "FAIA", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 1, "FAIA", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 3, "FAIC", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 1, "FAIC", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 7, "FAIA", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 5, "FAIA", 24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 7, "FAIC", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 5, "FAIC", 24., ycoor, zcoor, 0, "MANY"); - - zcoor = (366.9 + 198.8) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO3", 3, "FAIA", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 1, "FAIA", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 3, "FAIC", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 1, "FAIC", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 7, "FAIA", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 5, "FAIA", 24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 7, "FAIC", -24., ycoor, zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 5, "FAIC", 24., ycoor, zcoor, 0, "MANY"); + // Positioning of the longitudinal components of the SM cooling system, segmented between the + // FEA card containers rather than declared overlapping. + mBarPieces.clear(); + const std::vector contFull = feaContainers(zlenA, kFALSE); + const std::vector contHoles = feaContainers(zlenA, kTRUE); + const Double_t crateY = -(ytof * 0.5 - Geo::MODULECOVERTHICKNESS) * 0.5 + carpar[1]; + const Double_t crateY0 = crateY - carpar[1], crateY1 = crateY + carpar[1]; + const Float_t zcoor2 = (198.8 + 56.82) * 0.5; + const Float_t zcoor3 = (366.9 + 198.8) * 0.5; + Int_t copyA = 0, copyB = 0, copyC = 0; + + for (Int_t up = 0; up < 2; ++up) { + ycoor = up ? ytub + (tubepar[1] + 2. * bar2[1] + lonpar1[1]) : ytub - (tubepar[1] + 2. * bar2[1] + lonpar1[1]); + for (Int_t sx = -1; sx < 2; sx += 2) { + placeCoolingBar("FAIA", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar1[0], ycoor, lonpar1[1], 0., + lonpar1[2], copyA); + placeCoolingBar("FAIC", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar1[0], ycoor, lonpar1[1], 0., + lonpar1[2], copyC); + for (Int_t sz = -1; sz < 2; sz += 2) { + placeCoolingBar("FAIA", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar2[0], ycoor, lonpar2[1], + sz * zcoor2, lonpar2[2], copyA); + placeCoolingBar("FAIC", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar2[0], ycoor, lonpar2[1], + sz * zcoor2, lonpar2[2], copyC); + placeCoolingBar("FAIA", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar3[0], ycoor, lonpar3[1], + sz * zcoor3, lonpar3[2], copyA); + placeCoolingBar("FAIC", contFull, carpar[2], crateY0, crateY1, sx * 24., lonpar3[0], ycoor, lonpar3[1], + sz * zcoor3, lonpar3[2], copyC); + } + } + } Float_t carpos[3] = {static_cast(25. - xtof * 0.5), static_cast((11.5 - (ytof * 0.5 - Geo::MODULECOVERTHICKNESS)) * 0.5), 0.}; @@ -1460,20 +1526,17 @@ void Detector::makeSuperModuleCooling(Float_t xtof, Float_t ytof, Float_t zlenA) TVirtualMC::GetMC()->Gspos("FTLN", 5 + sg, "FAIB", 0., yFLTN, 56.82 * sg, 0, "ONLY"); } - ycoor = ytub + (tubepar[1] + 2. * bar2[1] + lonpar1[1]); - zcoor = (198.8 + 56.82) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO2", 2, "FAIB", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 1, "FAIB", -24., ycoor, zcoor, 0, "MANY"); - zcoor = (366.9 + 198.8) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO3", 2, "FAIB", -24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 1, "FAIB", -24., ycoor, zcoor, 0, "MANY"); - ycoor = ytub - (tubepar[1] + 2. * bar2[1] + lonpar1[1]); - zcoor = (198.8 + 56.82) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO2", 4, "FAIB", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO2", 3, "FAIB", 24., ycoor, zcoor, 0, "MANY"); - zcoor = (366.9 + 198.8) * 0.5; - TVirtualMC::GetMC()->Gspos("FLO3", 4, "FAIB", 24., ycoor, -zcoor, 0, "MANY"); - TVirtualMC::GetMC()->Gspos("FLO3", 3, "FAIB", 24., ycoor, zcoor, 0, "MANY"); + // the modules with the PHOS hole carry one x side per cooling layer, and no FLO1 bar + for (Int_t up = 0; up < 2; ++up) { + ycoor = up ? ytub + (tubepar[1] + 2. * bar2[1] + lonpar1[1]) : ytub - (tubepar[1] + 2. * bar2[1] + lonpar1[1]); + const Double_t xcoor = up ? -24. : 24.; + for (Int_t sz = -1; sz < 2; sz += 2) { + placeCoolingBar("FAIB", contHoles, carpar[2], crateY0, crateY1, xcoor, lonpar2[0], ycoor, lonpar2[1], + sz * zcoor2, lonpar2[2], copyB); + placeCoolingBar("FAIB", contHoles, carpar[2], crateY0, crateY1, xcoor, lonpar3[0], ycoor, lonpar3[1], + sz * zcoor3, lonpar3[2], copyB); + } + } } Float_t barS[3] = {Geo::BARS[0], Geo::BARS[1], Geo::BARS[2]};