Project
Loading...
Searching...
No Matches
Detector.cxx
Go to the documentation of this file.
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 <FairVolume.h>
13
14#include <TVirtualMC.h>
15#include <TVirtualMCStack.h>
16#include <TGeoVolume.h>
17#include <TGeoTube.h>
18#include <TGeoManager.h>
19
20#include "DetectorsBase/Stack.h"
23#include <set>
25
26using o2::itsmft::Hit;
27
28namespace o2::mi3
29{
30
32 : o2::base::DetImpl<Detector>("MI3", true),
33 mTrackData(),
34 mHits(o2::utils::createSimVector<o2::itsmft::Hit>())
35{
36}
37
39 : o2::base::DetImpl<Detector>("MI3", true),
40 mTrackData(),
41 mHits(o2::utils::createSimVector<o2::itsmft::Hit>())
42{
43 auto& midPars = MIDBaseParam::Instance();
44}
45
47{
48 if (mHits) {
50 }
51}
52
58
60{
61 int ifield = 2; // ?
62 float fieldm = 10.0; // ?
64
65 float tmaxfdPolys = 0.1; // .10000E+01; // Degree
66 float stemaxPolys = .10000E+01; // cm
67 float deemaxPolys = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
68 float epsilPolys = 1.0E-4; // .10000E+01;
69 float stminPolys = 0.0; // cm "Default value used"
70
71 // Materials
72 // Air
73 float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
74 float zAir[4] = {6., 7., 8., 18.};
75 float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
76 float dAir = 1.20479E-3;
77 float dAir1 = 1.20479E-10;
78
79 o2::base::Detector::Mixture(0, "AIR", aAir, zAir, dAir, 4, wAir);
80 o2::base::Detector::Medium(0, "AIR", 0, 0, ifield, fieldm, tmaxfdPolys, stemaxPolys, deemaxPolys, epsilPolys, stminPolys);
81
82 // Polystyrene (C6H5CHCH2)n https://pdg.lbl.gov/2023/AtomicNuclearProperties/HTML/polystyrene.html
83 float aPolys[2] = {1.0080, 12.0107};
84 float zPolys[2] = {1.f, 6};
85 float wPolys[2] = {0.077418, 0.922582};
86 float dPolys = 1.060; // g/cm3
87
88 o2::base::Detector::Mixture(1, "POLYSTYRENE", aPolys, zPolys, dPolys, 2, wPolys);
89 o2::base::Detector::Medium(1, "POLYSTYRENE", 1, 0, ifield, fieldm, tmaxfdPolys, stemaxPolys, deemaxPolys, epsilPolys, stminPolys);
90}
91
93{
94 LOG(info) << "Initialize MID O2Detector";
95 mGeometryTGeo = GeometryTGeo::Instance();
96 // Register sensitive volumes
97 TObjArray* allVols = gGeoManager->GetListOfVolumes();
98 TString sensorPattern = GeometryTGeo::getMIDSensorPattern();
99 std::set<TGeoVolume*> registered;
100 for (int i = 0; i < allVols->GetEntries(); i++) {
101 TGeoVolume* v = (TGeoVolume*)allVols->At(i);
102 TString vname = v->GetName();
103 if (vname.Contains(sensorPattern) && registered.find(v) == registered.end()) {
104 AddSensitiveVolume(v);
105 registered.insert(v);
106 }
107 }
108 LOGP(info, "Total MI3 sensitive volumes registered: {}", registered.size());
109}
110
112
114{
115 // This will create a branch in the output tree called Hit, setting the last
116 // parameter to kFALSE means that this collection will not be written to the file,
117 // it will exist only during the simulation
118
119 if (FairRootManager::Instance()) {
120 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true);
121 }
122}
123
125{
126 LOGP(info, "Creating MID geometry");
127
128 TGeoManager* geoManager = gGeoManager;
129 TGeoVolume* vALIC = geoManager->GetVolume("barrel");
130 if (!vALIC) {
131 LOGP(fatal, "Could not find barrel volume while constructing MID geometry");
132 }
133 new TGeoVolumeAssembly(GeometryTGeo::getMIDVolPattern());
134 TGeoVolume* vMID = geoManager->GetVolume(GeometryTGeo::getMIDVolPattern());
135 vALIC->AddNode(vMID, 2, new TGeoTranslation(0, 30., 0));
136
137 char vstrng[100] = "MIDVol";
138 vMID->SetTitle(vstrng);
139
140 // Build the MID
141 auto& midParam = MIDBaseParam::Instance();
142 const bool standardRadius = (midParam.mLayout == o2::mi3::MIDLayout::StandardRadius);
143
144 if (standardRadius) {
145 mLayers.resize(2);
146 mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 301.f, 500.f);
147 mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 311.f, 525.f); // 10 modules x 52.5 cm = 525 cm — matches Ian ref. code and SD Table 16 (10.5 m)
148 } else if (midParam.mLayout == o2::mi3::MIDLayout::SteppedLayout) {
149 // Ian Perez Garcia design (ICN-UNAM) — tesis §3.4.7 Geometria 8
150 // 11 cm gap from absorber outer face to MID layer
151 // mLayer index is flat 0-5: even = physical layer 0, odd = physical layer 1
152 // Module step: layer0=99.8cm (2x49.9), layer1=104cm (2x52=2xsumWidth)
153 // Central segment: Rmax_abso=290 -> Layer0=301, Layer1=311, nMod=6, semi-dz=299.4/312 at Z=0
154 // External segments: Rmax_abso=265 -> Layer0=276, Layer1=286, nMod=2, semi-dz=99.8/104 at Z=+-400
155 constexpr float kAbsGap = 11.f;
156 constexpr float kPitch = 10.f;
157 constexpr float kRCen0 = 290.f + kAbsGap; // 301 cm
158 constexpr float kRCen1 = kRCen0 + kPitch; // 311 cm
159 constexpr float kRExt0 = 265.f + kAbsGap; // 276 cm
160 constexpr float kRExt1 = kRExt0 + kPitch; // 286 cm
161 mLayers.resize(6);
162 // length = semi-length = nModulesZ x step (layer0: step=49.9cm, layer1: step=52cm)
163 mLayers[0] = MIDLayer(0, "MIDLayer0_central", kRCen0, 299.4f, 16, 0.f, 6); // 6 modules x 49.9 cm step
164 mLayers[1] = MIDLayer(1, "MIDLayer1_central", kRCen1, 312.f, 16, 0.f, 6); // 6 modules x 52 cm step
165 mLayers[2] = MIDLayer(2, "MIDLayer0_forward", kRExt0, 99.8f, 16, +400.f, 2, -1.f, 21); // 2 modules x 49.9 cm step, nBars=21 for R=276 cm
166 mLayers[3] = MIDLayer(3, "MIDLayer1_forward", kRExt1, 104.f, 16, +405.f, 2); // 2 modules x 52 cm step, +5 cm offset to clear absorber transition
167 mLayers[4] = MIDLayer(4, "MIDLayer0_backward", kRExt0, 99.8f, 16, -400.f, 2, -1.f, 21); // 2 modules x 49.9 cm step, nBars=21 for R=276 cm
168 mLayers[5] = MIDLayer(5, "MIDLayer1_backward", kRExt1, 104.f, 16, -405.f, 2); // 2 modules x 52 cm step, -5 cm offset to clear absorber transition
169 } else {
170 mLayers.resize(2);
171 mLayers[0] = MIDLayer(0, GeometryTGeo::composeSymNameLayer(0), 266.f, 500.f);
172 mLayers[1] = MIDLayer(1, GeometryTGeo::composeSymNameLayer(1), 276.f, 520.f);
173 }
174
175 for (auto& layer : mLayers) {
176 layer.createLayer(vMID);
177 }
178}
179
181{
182 if (!o2::utils::ShmManager::Instance().isOperational()) {
183 mHits->clear();
184 }
185 mTrackData.mHitStarted = false;
186}
187
188bool Detector::ProcessHits(FairVolume* vol)
189{
190 // This method is called from the MC stepping
191 if (!(fMC->TrackCharge())) {
192 return false;
193 }
194
195 int lay = vol->getVolumeId();
196 int volID = vol->getMCid();
197
198 // TrackReference block removed: ITS boilerplate whose condition (lay == 0
199 // against a TGeo volume ID) never fired. No MID reconstruction consumes
200 // MID track references at present.
201 auto stack = (o2::data::Stack*)fMC->GetStack();
202 // Extract physical layer index (0 or 1) from sensor name: MIDSensor_L<lay>_S...
203 int physLay = -1;
204 const char* volName = fMC->CurrentVolName();
205 sscanf(volName, "MIDSensor_L%d", &physLay);
206 if (physLay >= 0) {
207 physLay = physLay % 2;
208 }
209 bool startHit = false, stopHit = false;
210 unsigned char status = 0;
211 if (fMC->IsTrackEntering()) {
212 status |= Hit::kTrackEntering;
213 }
214 if (fMC->IsTrackInside()) {
215 status |= Hit::kTrackInside;
216 }
217 if (fMC->IsTrackExiting()) {
218 status |= Hit::kTrackExiting;
219 }
220 if (fMC->IsTrackOut()) {
221 status |= Hit::kTrackOut;
222 }
223 if (fMC->IsTrackStop()) {
224 status |= Hit::kTrackStopped;
225 }
226 if (fMC->IsTrackAlive()) {
227 status |= Hit::kTrackAlive;
228 }
229
230 // track is entering or created in the volume
231 if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) {
232 startHit = true;
233 } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) {
234 stopHit = true;
235 }
236
237 // increment energy loss at all steps except entrance
238 if (!startHit) {
239 mTrackData.mEnergyLoss += fMC->Edep();
240 }
241 if (!(startHit | stopHit)) {
242 return false; // do noting
243 }
244
245 if (startHit) {
246 mTrackData.mEnergyLoss = 0.;
247 fMC->TrackMomentum(mTrackData.mMomentumStart);
248 fMC->TrackPosition(mTrackData.mPositionStart);
249 mTrackData.mTrkStatusStart = status;
250 mTrackData.mHitStarted = true;
251 }
252 if (stopHit) {
253 TLorentzVector positionStop;
254 fMC->TrackPosition(positionStop);
255 // CurrentVolOffID(1..4) yields copy numbers of module/halfstave/stave ancestors.
256 // With TGeoVolumeAssembly nodes these are always 0 except the stave level.
257 // Full sensor location (layer, stave, module, bar) is encoded in the sensor
258 // name (MIDSensor_L<l>_S<s>_M<m>_B<b>) and can be decoded with sscanf if needed.
259 // Left as future work for hit digitization.
260
261 if (physLay < 0) {
262 LOGP(warn, "MID sensor name {} did not match expected pattern, cannot extract physical layer index", volName);
263 return false;
264 } // guard: sensor name did not match expected pattern
265 Hit* p = addHit(stack->GetCurrentTrackNumber(), physLay, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
266 mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
267 mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
268 // p->SetTotalEnergy(vmc->Etot());
269
270 // RS: not sure this is needed
271 // Increment number of Detector det points in TParticle
272 stack->addHit(GetDetId());
273 }
274
275 return true;
276}
277
278o2::itsmft::Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos,
279 const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus,
280 unsigned char endStatus)
281{
282 mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus);
283 return &(mHits->back());
284}
285} // namespace o2::mi3
Definition of the Stack class.
Definition of the ITSMFT Hit class.
int32_t i
ClassImp(IdPath)
uint32_t stack
Definition RawData.h:1
void Mixture(Int_t imat, const char *name, Float_t *a, Float_t *z, Float_t dens, Int_t nlmat, Float_t *wmat)
Definition Detector.cxx:66
void Medium(Int_t numed, const char *name, Int_t nmat, Int_t isvol, Int_t ifield, Float_t fieldm, Float_t tmaxfd, Float_t stemax, Float_t deemax, Float_t epsil, Float_t stmin, Float_t *ubuf=nullptr, Int_t nbuf=0)
Definition Detector.cxx:72
static void initFieldTrackingParams(int &mode, float &maxfield)
Definition Detector.cxx:143
std::string addNameTo(const char *ext) const
Definition Detector.h:150
std::vector< MIDLayer > mLayers
Definition Detector.h:84
void createMaterials()
Definition Detector.cxx:59
o2::itsmft::Hit * addHit(int trackID, int detID, const TVector3 &startPos, const TVector3 &endPos, const TVector3 &startMom, double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus)
Definition Detector.cxx:278
void ConstructGeometry() override
Definition Detector.cxx:53
void EndOfEvent() override
Definition Detector.cxx:111
bool ProcessHits(FairVolume *v=nullptr) override
Definition Detector.cxx:188
void InitializeO2Detector() override
Definition Detector.cxx:92
void Reset() override
Definition Detector.cxx:180
void Register() override
Definition Detector.cxx:113
static const char * getMIDVolPattern()
static const char * getMIDSensorPattern()
static const char * composeSymNameLayer(const int layer)
static GeometryTGeo * Instance()
static ShmManager & Instance()
Definition ShmManager.h:61
const GLdouble * v
Definition glcorearb.h:832
GLboolean * data
Definition glcorearb.h:298
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
@ SteppedLayout
@ StandardRadius
void freeSimVector(std::vector< T > *ptr)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Common utility functions.
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"