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
13
14#include "DetectorsBase/Stack.h"
15
16#include "TRKBase/Specs.h"
21#include <TGeoVolume.h>
22#include <TVirtualMC.h>
23#include <TVirtualMCStack.h>
24
25#include <FairVolume.h>
26
27#include <string>
28#include <type_traits>
29
30using o2::trkft3::Hit;
31
32namespace o2
33{
34namespace trk
35{
36
37float getDetLengthFromEta(const float eta, const float radius)
38{
39 return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta))));
40}
41
43 : o2::base::DetImpl<Detector>("TRK", true),
44 mTrackData(),
45 mHits(o2::utils::createSimVector<o2::trkft3::Hit>())
46{
47}
48
50 : o2::base::DetImpl<Detector>("TRK", true),
51 mTrackData(),
52 mHits(o2::utils::createSimVector<o2::trkft3::Hit>())
53{
54 auto& trkPars = TRKBaseParam::Instance();
55
56 if (trkPars.configFile != "") {
57 configFromFile(trkPars.configFile);
58 } else {
59 configMLOT();
62 }
63
64 LOGP(info, "Summary of TRK configuration:");
65 for (auto& layer : mLayers) {
66 LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer->getNumber(), layer->getName(), layer->getInnerRadius(), layer->getZ(), layer->getChipThickness());
67 }
68}
69
71 : o2::base::DetImpl<Detector>(other),
72 mTrackData(),
73 mHits(o2::utils::createSimVector<o2::trkft3::Hit>())
74{
75}
76
78{
79 if (mHits) {
81 }
82}
83
89
91{
92 auto& trkPars = TRKBaseParam::Instance();
93
94 mLayers.clear();
95
96 const std::vector<float> rInn{7.f, 9.f, 12.f, 20.f, 30.f, 45.f, 60.f, 80.f};
97 const float thick = 100.e-3;
98
99 switch (trkPars.layoutMLOT) {
100 case kCylindrical: {
101 const std::vector<float> length{127.985f, 127.985f, 127.985f, 127.985f, 127.985f, 255.9f, 255.9f, 255.9f};
102 LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK");
103 for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) {
105 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(i, name, rInn[i], length[i], thick, MatBudgetParamMode::Thickness));
106 }
107 break;
108 }
109 case kSegmented: {
110 const std::vector<float> tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f};
111 // const std::vector<float> tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f};
112 const std::vector<int> nStaves{10, 14, 18, 26, 38, 32, 42, 56};
113 // const std::vector<int> nStaves{10, 16, 22, 26, 38, 32, 42, 56};
114 const std::vector<int> nMods{11, 11, 11, 11, 11, 22, 22, 22};
115
116 const std::vector<float> stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f};
117
118 LOGP(warning, "Loading segmented configuration for ALICE3 TRK");
119 for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) {
122 mLayers.push_back(std::make_unique<TRKMLLayer>(i, name, rInn[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
123 } else {
124 mLayers.push_back(std::make_unique<TRKOTLayer>(i, name, rInn[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
125 }
126 }
127 break;
128 }
130 // Same ML as segmented; OT uses the detailed (realistic) barrel.
131 const std::vector<float> tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f};
132 const std::vector<int> nMods{11, 11, 11, 11, 11, 22, 22, 22};
133 const std::vector<float> stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f};
134
135 // OT: L9/L10 radii from the mechanical CAD, L8 at 442 mm; staves per ring (15/21/28 per quarter barrel)
136 std::vector<float> rInnReal = rInn;
137 rInnReal[constants::ML::nLayers + 0] = 44.2f;
138 rInnReal[constants::ML::nLayers + 1] = 62.0f;
139 rInnReal[constants::ML::nLayers + 2] = 82.0f;
140 const std::vector<int> nStaves{10, 14, 18, 26, 38, 30, 42, 56};
141
142 LOGP(warning, "Loading simplified-realistic configuration for ALICE3 TRK");
143 for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) {
146 mLayers.push_back(std::make_unique<TRKMLLayer>(i, name, rInnReal[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
147 } else {
148 mLayers.push_back(std::make_unique<TRKOTLayerRealistic>(i, name, rInnReal[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
149 }
150 }
151 break;
152 }
153 default:
154 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
155 break;
156 }
157}
158
159void Detector::configFromFile(std::string fileName)
160{
161 // Override the default geometry if config file provided
162 std::ifstream confFile(fileName);
163 if (!confFile.good()) {
164 LOGP(fatal, "File {} not found, aborting.", fileName);
165 }
166
167 auto& trkPars = TRKBaseParam::Instance();
168
169 mLayers.clear();
170
171 LOGP(info, "Overriding geometry of ALICE3 TRK using {} file.", fileName);
172
173 std::string line;
174 std::vector<float> tmpBuff;
175 int layerCount{0};
176 while (std::getline(confFile, line)) {
177 if (line[0] == '/') {
178 continue;
179 }
180 tmpBuff.clear();
181 std::stringstream ss(line);
182 float val;
183 std::string substr;
184 while (getline(ss, substr, '\t')) {
185 tmpBuff.push_back(std::stof(substr));
186 }
187
188 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount);
189
190 switch (trkPars.layoutMLOT) {
191 case kCylindrical: {
192 // Expected column mapping in the text file (separated by \t):
193 // tmpBuff[0] = rInn
194 // tmpBuff[1] = length
195 // tmpBuff[2] = thick
196 // tmpBuff[3] = matBudgetMode (optional, default = Thickness)
197
198 // Cylindrical requires at least 3 parameters
199 if (tmpBuff.size() < 3) {
200 LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount);
201 }
202
203 float rInn = tmpBuff[0];
204 float length = tmpBuff[1];
205 float thick = tmpBuff[2];
206
207 // Default mode is Thickness
209 if (tmpBuff.size() >= 4) {
210 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[3]));
211 }
212
213 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(layerCount, name, rInn, length, thick, matBudgetMode));
214 break;
215 }
216 case kSegmented:
218 // Expected column mapping in the text file (separated by \t):
219 // tmpBuff[0] = rInn
220 // tmpBuff[1] = thick
221 // tmpBuff[2] = tiltAngle
222 // tmpBuff[3] = nStaves
223 // tmpBuff[4] = nMods
224 // tmpBuff[5] = stagOffset (required ONLY for ML)
225 // tmpBuff[6] = matBudgetMode (optional, default = Thickness)
226 // tmpBuff[7] = rInnRow (optional, ML only, default = 0): which staves of a staggered layer sit at
227 // rInn, 0 = even, 1 = odd (needs tmpBuff[6]); in the flipped layer the rInn row is the outer one
228 // tmpBuff[8] = phiOffset (optional, ML only, default = 0): rotation of all the staves of the layer about
229 // the beam axis, in degrees, counterclockwise as phi (needs tmpBuff[6] and tmpBuff[7])
230
231 // Base parameters for all segmented layers (at least 5 needed)
232 if (tmpBuff.size() < 5) {
233 LOGP(fatal, "Invalid configuration for segmented layer {}: missing base parameters.", layerCount);
234 }
235
236 float rInn = tmpBuff[0];
237 float thick = tmpBuff[1];
238 float tiltAngle = tmpBuff[2];
239 int nStaves = static_cast<int>(tmpBuff[3]);
240 int nMods = static_cast<int>(tmpBuff[4]);
241
242 // Default mode is Thickness
244
245 if (layerCount < constants::ML::nLayers) {
246 // ML layers require stagOffset (index 5)
247 if (tmpBuff.size() < 6) {
248 LOGP(fatal, "Invalid configuration for ML layer {}: stagOffset is missing.", layerCount);
249 }
250 float stagOffset = tmpBuff[5];
251
252 if (tmpBuff.size() >= 7) {
253 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[6]));
254 }
255 const int rInnRow = (tmpBuff.size() >= 8) ? static_cast<int>(tmpBuff[7]) : 0;
256 const float phiOffset = (tmpBuff.size() >= 9) ? tmpBuff[8] : 0.f;
257
258 mLayers.push_back(std::make_unique<TRKMLLayer>(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, matBudgetMode, rInnRow, phiOffset));
259 } else {
260 // OT layers do NOT have stagOffset. The optional mode is at index 5.
261 if (tmpBuff.size() >= 6) {
262 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[5]));
263 }
264
265 if (trkPars.layoutMLOT == kSimplifiedRealistic) {
266 mLayers.push_back(std::make_unique<TRKOTLayerRealistic>(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, matBudgetMode));
267 } else {
268 mLayers.push_back(std::make_unique<TRKOTLayer>(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, matBudgetMode));
269 }
270 }
271 break;
272 }
273 default:
274 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
275 break;
276 }
277
278 ++layerCount;
279 }
280}
281
282void Detector::configToFile(std::string fileName)
283{
284 LOGP(info, "Exporting TRK Detector layout to {}", fileName);
285 std::ofstream conFile(fileName.c_str(), std::ios::out);
286 conFile << "/// TRK configuration file: inn_radius z_length lay_thickness" << std::endl;
287 for (const auto& layer : mLayers) {
288 conFile << layer->getInnerRadius() << "\t" << layer->getZ() << "\t" << layer->getChipThickness() << std::endl;
289 }
290}
291
293{
294 mServices = TRKServices();
295}
296
298{
299 int ifield = 2; // ?
300 float fieldm = 10.0; // ?
302
303 float tmaxfdSi = 0.1; // .10000E+01; // Degree
304 float stemaxSi = 0.0075; // .10000E+01; // cm
305 float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
306 float epsilSi = 1.0E-4; // .10000E+01;
307 float stminSi = 0.0; // cm "Default value used"
308
309 float tmaxfdAir = 0.1; // .10000E+01; // Degree
310 float stemaxAir = .10000E+01; // cm
311 float deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
312 float epsilAir = 1.0E-4; // .10000E+01;
313 float stminAir = 0.0; // cm "Default value used"
314
315 float tmaxfdCer = 0.1; // .10000E+01; // Degree
316 float stemaxCer = .10000E+01; // cm
317 float deemaxCer = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
318 float epsilCer = 1.0E-4; // .10000E+01;
319 float stminCer = 0.0; // cm "Default value used"
320
321 // Tracking parameters shared by the passive materials below
322 float tmaxfdPas = 0.1;
323 float stemaxPas = 1.0;
324 float deemaxPas = 0.1;
325 float epsilPas = 1.0E-4;
326 float stminPas = 0.0;
327
328 // AIR
329 float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
330 float zAir[4] = {6., 7., 8., 18.};
331 float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
332 float dAir = 1.20479E-3;
333
334 // Carbon fiber
335 float aCf[2] = {12.0107, 1.00794};
336 float zCf[2] = {6., 1.};
337
338 // FPC: Kapton+Cu effective mixture, X0 ~ 5 cm
339 float aFpc[2] = {63.546f, 12.0107f}; // Cu, C (Kapton proxy)
340 float zFpc[2] = {29.f, 6.f};
341 float wFpc[2] = {0.40f, 0.60f};
342 float dFpc = 3.4f;
343
344 // ZIF connector: LCP+Cu effective mixture, X0 ~ 2.9 cm
345 float aLcpCu[2] = {63.546f, 12.0107f};
346 float zLcpCu[2] = {29.f, 6.f};
347 float wLcpCu[2] = {0.60f, 0.40f};
348 float dLcpCu = 5.5f;
349
350 // SMD capacitors: BaTiO3 ceramic, X0 ~ 1.9 cm
351 float aBaTiO3[3] = {137.327f, 47.867f, 15.9994f};
352 float zBaTiO3[3] = {56.f, 22.f, 8.f};
353 float wBaTiO3[3] = {0.5879f, 0.2054f, 0.2067f};
354 float dBaTiO3 = 6.0f;
355
356 // FR4 (PCB laminate) for the end-of-stave cards: 60% glass (SiO2) + 40% epoxy by weight
357 float aFr4[4] = {28.0855f, 15.9994f, 12.0107f, 1.00794f}; // Si, O, C, H
358 float zFr4[4] = {14.f, 8.f, 6.f, 1.f};
359 float wFr4[4] = {0.2804f, 0.3836f, 0.3040f, 0.0320f};
360 float dFr4 = 1.85f;
361
362 o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir);
363 o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir);
364
365 o2::base::Detector::Material(3, "SILICON$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03);
366 o2::base::Detector::Medium(3, "SILICON$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi);
367
368 // Carbon fibre: density tuned so X0 ~ 27 cm
369 o2::base::Detector::Material(4, "CARBONFIBER$", 12.0107f, 6.f, 1.45f, 27.0f, 999.f);
370 o2::base::Detector::Medium(4, "CARBONFIBER$", 4, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
371
372 o2::base::Detector::Mixture(5, "FPC$", aFpc, zFpc, dFpc, 2, wFpc);
373 o2::base::Detector::Medium(5, "FPC$", 5, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
374
375 o2::base::Detector::Mixture(6, "LCPCU$", aLcpCu, zLcpCu, dLcpCu, 2, wLcpCu);
376 o2::base::Detector::Medium(6, "LCPCU$", 6, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
377
378 o2::base::Detector::Mixture(7, "BATIO3$", aBaTiO3, zBaTiO3, dBaTiO3, 3, wBaTiO3);
379 o2::base::Detector::Medium(7, "BATIO3$", 7, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
380
381 // PEEK polymer for mounting brackets: X0 ~ 20 cm
382 o2::base::Detector::Material(8, "PEEK$", 12.0107f, 6.f, 1.32f, 20.0f, 999.f);
383 o2::base::Detector::Medium(8, "PEEK$", 8, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
384
385 o2::base::Detector::Mixture(9, "FR4$", aFr4, zFr4, dFr4, 4, wFr4);
386 o2::base::Detector::Medium(9, "FR4$", 9, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
387
388 // Copper planes of the end-of-stave cards: X0 = 1.436 cm
389 o2::base::Detector::Material(10, "COPPER$", 63.546f, 29.f, 8.96f, 1.436f, 999.f);
390 o2::base::Detector::Medium(10, "COPPER$", 10, 0, ifield, fieldm, tmaxfdPas, stemaxPas, deemaxPas, epsilPas, stminPas);
391}
392
394{
395 TGeoManager* geoManager = gGeoManager;
396 TGeoVolume* vALIC = geoManager->GetVolume("barrel");
397 if (!vALIC) {
398 LOGP(fatal, "Could not find barrel volume while constructing TRK geometry");
399 }
400 new TGeoVolumeAssembly(GeometryTGeo::getTRKVolPattern());
401 TGeoVolume* vTRK = geoManager->GetVolume(GeometryTGeo::getTRKVolPattern());
402 vALIC->AddNode(vTRK, 2, new TGeoTranslation(0, 30., 0));
403
404 char vstrng[100] = "TRKVol";
405 vTRK->SetTitle(vstrng);
406
407 for (auto& layer : mLayers) {
408 layer->createLayer(vTRK);
409 }
410
411 // Add service for inner tracker
412 mServices.createServices(vTRK);
413
414 // Build the VD using the petal builder
415 // Choose the VD design based on TRKBaseParam.layoutVD
416 auto& trkPars = TRKBaseParam::Instance();
417
419
420 switch (trkPars.layoutVD) {
421 case kIRIS4:
422 LOG(info) << "Building VD with IRIS4 layout";
424 break;
425 case kIRISFullCyl:
426 LOG(info) << "Building VD with IRIS fully cylindrical layout";
428 break;
430 LOG(info) << "Building VD with IRIS fully cylindrical layout with 3 inclined walls";
432 break;
433 case kIRIS5:
434 LOG(info) << "Building VD with IRIS5 layout";
436 break;
437 case kIRIS4a:
438 LOG(info) << "Building VD with IRIS4a layout";
440 break;
441 default:
442 LOG(fatal) << "Unknown VD layout option: " << static_cast<int>(trkPars.layoutVD);
443 break;
444 }
445
446 // Fill sensor names from registry right after geometry creation
447 const auto& regs = o2::trk::vdSensorRegistry();
448 mNumberOfVolumesVD = static_cast<int>(regs.size());
449 mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size();
450 mSensorName.resize(mNumberOfVolumes);
451
452 // Fill VD sensor names from registry
453 int VDvolume = 0;
454 for (const auto& sensor : regs) {
455 mSensorName[VDvolume] = sensor.name;
456 VDvolume++;
457 }
458
459 // Add MLOT sensor names
460 for (int i = 0; i < mLayers.size(); i++) {
461 mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i);
462 }
463
464 for (auto vd : mSensorName) {
465 std::cout << "Volume name: " << vd << std::endl;
466 }
467
468 mServices.excavateFromVacuum("IRIS_CUTOUTsh");
469 mServices.registerVacuum(vTRK);
470}
471
473{
474 LOG(info) << "Initialize TRK O2Detector";
475 mGeometryTGeo = GeometryTGeo::Instance();
476 defineSensitiveVolumes();
477
478 mSensorID.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different namingh scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12
479 for (int i = 0; i < mNumberOfVolumes; i++) {
480 mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry
481 LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data());
482 }
483}
484
485void Detector::defineSensitiveVolumes()
486{
487 TGeoManager* geoManager = gGeoManager;
488 TGeoVolume* v;
489
490 TString volumeName;
491 LOGP(info, "Adding TRK Sensitive Volumes");
492
493 // Register VD sensors created by VDGeometryBuilder
494 for (const auto& s : o2::trk::vdSensorRegistry()) {
495 TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str());
496 if (!v) {
497 LOGP(warning, "VD sensor volume '{}' not found", s.name);
498 continue;
499 }
500 LOGP(info, "Adding VD Sensitive Volume {}", v->GetName());
501 AddSensitiveVolume(v);
502 // Optionally track first/last layers for TR references:
503 if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) {
504 mFirstOrLastLayers.push_back(s.name);
505 }
506 }
507
508 // The names of the TRK sensitive volumes have the format: TRKLayer(0...mLayers.size()-1)
509 for (int j{0}; j < mLayers.size(); j++) {
510 volumeName = GeometryTGeo::getTRKSensorPattern() + TString::Itoa(j, 10);
511 if (j == mLayers.size() - 1) {
512 mFirstOrLastLayers.push_back(volumeName.Data());
513 }
514 LOGP(info, "Trying {}", volumeName.Data());
515 v = geoManager->GetVolume(volumeName.Data());
516 LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName());
517 AddSensitiveVolume(v);
518 }
519}
520
522
524{
525 // This will create a branch in the output tree called Hit, setting the last
526 // parameter to kFALSE means that this collection will not be written to the file,
527 // it will exist only during the simulation
528
529 if (FairRootManager::Instance()) {
530 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true);
531 }
532}
533
535{
536 if (!o2::utils::ShmManager::Instance().isOperational()) {
537 mHits->clear();
538 }
539}
540
541bool Detector::InsideFirstOrLastLayer(std::string layerName)
542{
543 bool inside = false;
544 for (auto& firstOrLastLayer : mFirstOrLastLayers) {
545 if (firstOrLastLayer == layerName) {
546 inside = true;
547 break;
548 }
549 }
550 return inside;
551}
552
553bool Detector::ProcessHits(FairVolume* vol)
554{
555 // This method is called from the MC stepping
556 if (!(fMC->TrackCharge())) {
557 return false;
558 }
559
560 int subDetID = -1;
561 int layer = -1;
562 int volume = 0;
563 int volID = vol->getMCid();
564
565 bool notSens = false;
566 while ((volume < mNumberOfVolumes) && (notSens = (volID != mSensorID[volume]))) {
567 ++volume;
568 }
569
570 if (notSens) {
571 return kFALSE; // RS: can this happen? This method must be called for sensors only?
572 }
573
574 if (volume < mNumberOfVolumesVD) {
575 subDetID = 0; // VD. For the moment each "chip" is a volume./// TODO: change this logic once the naming scheme is changed
576 } else {
577 subDetID = 1; // MLOT
578 layer = volume - mNumberOfVolumesVD;
579 }
580
581 // Is it needed to keep a track reference when the outer ITS volume is encountered?
582 auto stack = (o2::data::Stack*)fMC->GetStack();
583 // if (fMC->IsTrackExiting() && (lay == 0 || lay == mLayers.size() - 1)) {
584 if (fMC->IsTrackExiting() && InsideFirstOrLastLayer(vol->GetName())) {
585 // Keep the track refs for the innermost and outermost layers only
586 o2::TrackReference tr(*fMC, GetDetId());
587 tr.setTrackID(stack->GetCurrentTrackNumber());
588 tr.setUserId(volume);
589 stack->addTrackReference(tr);
590 }
591 bool startHit = false, stopHit = false;
592 unsigned char status = 0;
593 if (fMC->IsTrackEntering()) {
594 status |= Hit::kTrackEntering;
595 }
596 if (fMC->IsTrackInside()) {
597 status |= Hit::kTrackInside;
598 }
599 if (fMC->IsTrackExiting()) {
600 status |= Hit::kTrackExiting;
601 }
602 if (fMC->IsTrackOut()) {
603 status |= Hit::kTrackOut;
604 }
605 if (fMC->IsTrackStop()) {
606 status |= Hit::kTrackStopped;
607 }
608 if (fMC->IsTrackAlive()) {
609 status |= Hit::kTrackAlive;
610 }
611
612 // track is entering or created in the volume
613 if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) {
614 startHit = true;
615 } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) {
616 stopHit = true;
617 }
618
619 // increment energy loss at all steps except entrance
620 if (!startHit) {
621 mTrackData.mEnergyLoss += fMC->Edep();
622 }
623 if (!(startHit | stopHit)) {
624 return false; // do noting
625 }
626
627 if (startHit) {
628 mTrackData.mEnergyLoss = 0.;
629 fMC->TrackMomentum(mTrackData.mMomentumStart);
630 fMC->TrackPosition(mTrackData.mPositionStart);
631 mTrackData.mTrkStatusStart = status;
632 mTrackData.mHitStarted = true;
633 }
634 if (stopHit) {
635 TLorentzVector positionStop;
636 fMC->TrackPosition(positionStop);
637
638 // Retrieve the indices with the volume path
639 int stave(0), halfstave(0), mod(0), chip(0);
640
641 auto& trkPars = TRKBaseParam::Instance();
642
643 if (subDetID == 1) {
644 if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kSegmented) {
645 fMC->CurrentVolOffID(1, chip);
646 fMC->CurrentVolOffID(2, mod);
647 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) {
648 fMC->CurrentVolOffID(3, halfstave);
649 fMC->CurrentVolOffID(4, stave);
650 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) {
651 fMC->CurrentVolOffID(3, stave);
652 } else {
653 LOGP(fatal, "Wrong number of halfstaves for layer {}", layer);
654 }
655 } else if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kSimplifiedRealistic) {
656 // Stave/half-stave/module are assembly volumes here, for which CurrentVolOffID copy
657 // numbers all read 0; resolve the indices from the TGeo path at the hit mid-point.
658 const TVector3 mid = (mTrackData.mPositionStart.Vect() + positionStop.Vect()) * 0.5;
659 gGeoManager->PushPath();
660 if (gGeoManager->FindNode(mid.X(), mid.Y(), mid.Z())) {
661 auto copyUp = [](int up) { TGeoNode* n = gGeoManager->GetMother(up); return n ? n->GetNumber() : 0; };
662 chip = copyUp(1);
663 mod = copyUp(2);
664 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) {
665 halfstave = copyUp(3);
666 stave = copyUp(4);
667 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) {
668 stave = copyUp(3);
669 } else {
670 LOGP(fatal, "Wrong number of halfstaves for layer {}", layer);
671 }
672 }
673 gGeoManager->PopPath();
674 }
675 }
676
677 unsigned short chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave, mod, chip);
678
679 // Print(vol, volume, subDetID, layer, stave, halfstave, mod, chip, chipID);
680
681 // mGeometryTGeo->Print();
682
683 Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
684 mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
685 mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
686 // p->SetTotalEnergy(vmc->Etot());
687
688 // RS: not sure this is needed
689 // Increment number of Detector det points in TParticle
690 stack->addHit(GetDetId());
691 }
692
693 return true;
694}
695
696o2::trkft3::Hit* Detector::addHit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos,
697 const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus,
698 unsigned char endStatus)
699{
700 mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus);
701 return &(mHits->back());
702}
703
704void Detector::Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const
705{
706 int currentVol(0);
707 LOG(info) << "Current volume name: " << fMC->CurrentVolName() << " and ID " << fMC->CurrentVolID(currentVol);
708 LOG(info) << "volume: " << volume << "/" << mNumberOfVolumes - 1;
709
710 auto& trkPars = TRKBaseParam::Instance();
711
712 if (subDetID == 1) { // MLOT
713 if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kCylindrical) {
714 LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip;
715 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " Chip ID: " << chipID;
716 } else {
717 LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip;
718 LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod;
719 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry
720 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave;
721 LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave;
722 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
723 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry
724 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave;
725 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
726 }
727 }
728 } else {
729 // VD
730 LOG(info) << "SubDetector ID: " << subDetID << " Chip ID: " << chipID;
731 }
732
733 LOG(info);
734}
735
736} // namespace trk
737} // namespace o2
738
740
741// Define Factory method for calling from the outside
742extern "C" {
747}
Definition of the Stack class.
int32_t i
uint32_t j
Definition RawData.h:0
uint32_t stack
Definition RawData.h:1
specs of the ALICE3 TRK
double radius
ClassImp(o2::trk::Detector)
o2::base::Detector * create_detector_trk(bool active)
Definition Detector.cxx:743
void setUserId(Int_t userId)
void setTrackID(Int_t track)
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
void Material(Int_t imat, const char *name, Float_t a, Float_t z, Float_t dens, Float_t radl, Float_t absl, Float_t *buf=nullptr, Int_t nwbuf=0)
Definition Detector.cxx:59
std::string addNameTo(const char *ext) const
Definition Detector.h:151
void Reset() override
Definition Detector.cxx:534
void Register() override
Definition Detector.cxx:523
void ConstructGeometry() override
Definition Detector.cxx:84
void InitializeO2Detector() override
Definition Detector.cxx:472
o2::trkft3::Hit * addHit(int trackID, unsigned short 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:696
void configFromFile(std::string fileName="alice3_TRK_layout.txt")
Definition Detector.cxx:159
void EndOfEvent() override
Definition Detector.cxx:521
void Print(FairVolume *vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const
Number of TRK layers.
Definition Detector.cxx:704
static o2::base::Detector * create(bool active)
Definition Detector.h:39
void configToFile(std::string fileName="alice3_TRK_layout.txt")
Definition Detector.cxx:282
bool ProcessHits(FairVolume *v=nullptr) override
Definition Detector.cxx:553
std::vector< int > mSensorID
Definition Detector.h:101
std::vector< TString > mSensorName
layer identifiers
Definition Detector.h:102
static const char * getTRKSensorPattern()
static const char * getTRKLayerPattern()
unsigned short getChipIndex(int subDetID, int petalcase, int disk, int lay, int stave, int halfstave, int mod, int chip) const
int getNumberOfHalfStaves(int lay) const
static const char * getTRKVolPattern()
static GeometryTGeo * Instance()
void registerVacuum(TGeoVolume *motherVolume)
void createServices(TGeoVolume *motherVolume)
void excavateFromVacuum(TString shapeToExcavate)
static ShmManager & Instance()
Definition ShmManager.h:61
GLdouble n
Definition glcorearb.h:1982
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLuint GLfloat * val
Definition glcorearb.h:1582
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
constexpr int nLayers
Definition Specs.h:105
constexpr int nLayers
Definition Specs.h:133
std::vector< VDSensorDesc > & vdSensorRegistry()
float getDetLengthFromEta(const float eta, const float radius)
Definition Detector.cxx:37
void createIRISGeometry3InclinedWalls(TGeoVolume *motherVolume)
void createIRISGeometryFullCyl(TGeoVolume *motherVolume)
void createIRIS4aGeometry(TGeoVolume *motherVolume)
MatBudgetParamMode
Definition TRKLayer.h:27
void createIRIS4Geometry(TGeoVolume *motherVolume)
void createIRIS5Geometry(TGeoVolume *motherVolume)
@ kIRISFullCyl3InclinedWalls
void clearVDSensorRegistry()
@ kSimplifiedRealistic
void freeSimVector(std::vector< T > *ptr)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
Common utility functions.
VectorOfTObjectPtrs other
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"