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"
18#include "TRKSimulation/Hit.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::trk::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::trk::Hit>())
46{
47}
48
49Detector::Detector(bool active)
50 : o2::base::DetImpl<Detector>("TRK", true),
51 mTrackData(),
52 mHits(o2::utils::createSimVector<o2::trk::Hit>())
53{
54 auto& trkPars = TRKBaseParam::Instance();
55
56 if (trkPars.configFile != "") {
57 configFromFile(trkPars.configFile);
58 } else {
59 configMLOT();
60 if (!trkPars.disableFT3) {
62 }
65 }
66
67 LOGP(info, "Summary of TRK configuration:");
68 for (auto& layer : mLayers) {
69 LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer->getNumber(), layer->getName(), layer->getInnerRadius(), layer->getZ(), layer->getChipThickness());
70 }
71}
72
74 : o2::base::DetImpl<Detector>(other),
75 mTrackData(),
76 mHits(o2::utils::createSimVector<o2::trk::Hit>())
77{
78}
79
81{
82 if (mHits) {
84 }
85}
86
87void Detector::ConstructGeometry()
88{
91}
92
93void Detector::configMLOT()
94{
95 auto& trkPars = TRKBaseParam::Instance();
96
97 mLayers.clear();
98
99 const std::vector<float> rInn{7.f, 9.f, 12.f, 20.f, 30.f, 45.f, 60.f, 80.f};
100 const float thick = 100.e-3;
101
102 switch (trkPars.layoutMLOT) {
103 case kCylindrical: {
104 const std::vector<float> length{127.985f, 127.985f, 127.985f, 127.985f, 127.985f, 255.9f, 255.9f, 255.9f};
105 LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK");
106 for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) {
107 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i);
108 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(i, name, rInn[i], length[i], thick, MatBudgetParamMode::Thickness));
109 }
110 break;
111 }
112 case kSegmented: {
113 const std::vector<float> tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f};
114 // const std::vector<float> tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f};
115 const std::vector<int> nStaves{10, 14, 18, 26, 38, 32, 42, 56};
116 // const std::vector<int> nStaves{10, 16, 22, 26, 38, 32, 42, 56};
117 const std::vector<int> nMods{11, 11, 11, 11, 11, 22, 22, 22};
118
119 const std::vector<float> stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f};
120
121 LOGP(warning, "Loading segmented configuration for ALICE3 TRK");
122 for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) {
123 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i);
124 if (i < constants::ML::nLayers) {
125 mLayers.push_back(std::make_unique<TRKMLLayer>(i, name, rInn[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
126 } else {
127 mLayers.push_back(std::make_unique<TRKOTLayer>(i, name, rInn[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness));
128 }
129 }
130 break;
131 }
132 default:
133 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
134 break;
135 }
136}
137
138void Detector::configFT3ScopingV3()
139{
140 // Build the FT3 detector according to v3 layout
141 // https://indico.cern.ch/event/1596309/contributions/6728167/attachments/3190117/5677220/2025-12-10-AW-ALICE3planning.pdf
142 // Middle disks inner radius 10 cm
143 // Outer disks inner radius 20 cm
144
145 LOG(info) << "Building FT3 Detector: v3 scoping version";
146
147 const int numberOfLayers = 6;
148 const float sensorThickness = 30.e-4;
149 const float layersx2X0 = 1.e-2;
150 using LayerConfig = std::array<float, 4>; // {z_layer, r_in, r_out, Layerx2X0}
151 const std::array<LayerConfig, numberOfLayers> layersConfigCSide{LayerConfig{77., 10.0, 35., layersx2X0},
152 LayerConfig{100., 10.0, 35., layersx2X0},
153 LayerConfig{122., 10.0, 35., layersx2X0},
154 LayerConfig{150., 20.0, 68.f, layersx2X0},
155 LayerConfig{180., 20.0, 68.f, layersx2X0},
156 LayerConfig{220., 20.0, 68.f, layersx2X0}};
157
158 const std::array<LayerConfig, numberOfLayers> layersConfigASide{LayerConfig{77., 10.0, 35., layersx2X0},
159 LayerConfig{100., 10.0, 35., layersx2X0},
160 LayerConfig{122., 10.0, 35., layersx2X0},
161 LayerConfig{150., 20.0, 68.f, layersx2X0},
162 LayerConfig{180., 20.0, 68.f, layersx2X0},
163 LayerConfig{220., 20.0, 68.f, layersx2X0}};
164 const std::array<bool, numberOfLayers> enabled{true, true, true, true, true, true}; // To enable or disable layers for debug purpose
165
166 for (int direction : {kBackward, kForward}) {
167 mFT3LayerName[direction].clear();
168 const std::array<LayerConfig, numberOfLayers>& layerConfig = (direction == kBackward) ? layersConfigCSide : layersConfigASide;
169 for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) {
170 if (!enabled[layerNumber]) {
171 continue;
172 }
173 const std::string directionName = std::to_string(direction);
174 const std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber);
175 mFT3LayerName[direction].push_back(layerName.c_str());
176 const float z = layerConfig[layerNumber][0];
177 const float rIn = layerConfig[layerNumber][1];
178 const float rOut = layerConfig[layerNumber][2];
179 const float x0 = layerConfig[layerNumber][3];
180 LOG(info) << "buildFT3ScopingV3 -> Adding Layer " << layerNumber << "/" << numberOfLayers << " " << layerName << " at z = " << z;
181 // Add layers
182 const bool isMiddleLayer = layerNumber < 3;
183 auto& thisLayer = mFT3Layers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer);
184 }
185 }
186}
187
188void Detector::configFromFile(std::string fileName)
189{
190 // Override the default geometry if config file provided
191 std::ifstream confFile(fileName);
192 if (!confFile.good()) {
193 LOGP(fatal, "File {} not found, aborting.", fileName);
194 }
195
196 auto& trkPars = TRKBaseParam::Instance();
197
198 mLayers.clear();
199
200 LOGP(info, "Overriding geometry of ALICE3 TRK using {} file.", fileName);
201
202 std::string line;
203 std::vector<float> tmpBuff;
204 int layerCount{0};
205 while (std::getline(confFile, line)) {
206 if (line[0] == '/') {
207 continue;
208 }
209 tmpBuff.clear();
210 std::stringstream ss(line);
211 float val;
212 std::string substr;
213 while (getline(ss, substr, '\t')) {
214 tmpBuff.push_back(std::stof(substr));
215 }
216
217 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount);
218
219 switch (trkPars.layoutMLOT) {
220 case kCylindrical: {
221 // Expected column mapping in the text file (separated by \t):
222 // tmpBuff[0] = rInn
223 // tmpBuff[1] = length
224 // tmpBuff[2] = thick
225 // tmpBuff[3] = matBudgetMode (optional, default = Thickness)
226
227 // Cylindrical requires at least 3 parameters
228 if (tmpBuff.size() < 3) {
229 LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount);
230 }
231
232 float rInn = tmpBuff[0];
233 float length = tmpBuff[1];
234 float thick = tmpBuff[2];
235
236 // Default mode is Thickness
237 MatBudgetParamMode matBudgetMode = MatBudgetParamMode::Thickness;
238 if (tmpBuff.size() >= 4) {
239 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[3]));
240 }
241
242 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(layerCount, name, rInn, length, thick, matBudgetMode));
243 break;
244 }
245 case kSegmented: {
246 // Expected column mapping in the text file (separated by \t):
247 // tmpBuff[0] = rInn
248 // tmpBuff[1] = thick
249 // tmpBuff[2] = tiltAngle
250 // tmpBuff[3] = nStaves
251 // tmpBuff[4] = nMods
252 // tmpBuff[5] = stagOffset (required ONLY for ML)
253 // tmpBuff[6] = matBudgetMode (optional, default = Thickness)
254
255 // Base parameters for all segmented layers (at least 5 needed)
256 if (tmpBuff.size() < 5) {
257 LOGP(fatal, "Invalid configuration for segmented layer {}: missing base parameters.", layerCount);
258 }
259
260 float rInn = tmpBuff[0];
261 float thick = tmpBuff[1];
262 float tiltAngle = tmpBuff[2];
263 int nStaves = static_cast<int>(tmpBuff[3]);
264 int nMods = static_cast<int>(tmpBuff[4]);
265
266 // Default mode is Thickness
267 MatBudgetParamMode matBudgetMode = MatBudgetParamMode::Thickness;
268
269 if (layerCount < constants::ML::nLayers) {
270 // ML layers require stagOffset (index 5)
271 if (tmpBuff.size() < 6) {
272 LOGP(fatal, "Invalid configuration for ML layer {}: stagOffset is missing.", layerCount);
273 }
274 float stagOffset = tmpBuff[5];
275
276 if (tmpBuff.size() >= 7) {
277 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[6]));
278 }
279
280 mLayers.push_back(std::make_unique<TRKMLLayer>(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, matBudgetMode));
281 } else {
282 // OT layers do NOT have stagOffset. The optional mode is at index 5.
283 if (tmpBuff.size() >= 6) {
284 matBudgetMode = static_cast<MatBudgetParamMode>(static_cast<int>(tmpBuff[5]));
285 }
286
287 mLayers.push_back(std::make_unique<TRKOTLayer>(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, matBudgetMode));
288 }
289 break;
290 }
291 default:
292 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
293 break;
294 }
295
296 ++layerCount;
297 }
298}
299
300void Detector::configToFile(std::string fileName)
301{
302 LOGP(info, "Exporting TRK Detector layout to {}", fileName);
303 std::ofstream conFile(fileName.c_str(), std::ios::out);
304 conFile << "/// TRK configuration file: inn_radius z_length lay_thickness" << std::endl;
305 for (const auto& layer : mLayers) {
306 conFile << layer->getInnerRadius() << "\t" << layer->getZ() << "\t" << layer->getChipThickness() << std::endl;
307 }
308}
309
310void Detector::configServices()
311{
312 mServices = TRKServices();
313}
314
315void Detector::createMaterials()
316{
317 int ifield = 2; // ?
318 float fieldm = 10.0; // ?
320
321 float tmaxfdSi = 0.1; // .10000E+01; // Degree
322 float stemaxSi = 0.0075; // .10000E+01; // cm
323 float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
324 float epsilSi = 1.0E-4; // .10000E+01;
325 float stminSi = 0.0; // cm "Default value used"
326
327 float tmaxfdAir = 0.1; // .10000E+01; // Degree
328 float stemaxAir = .10000E+01; // cm
329 float deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
330 float epsilAir = 1.0E-4; // .10000E+01;
331 float stminAir = 0.0; // cm "Default value used"
332
333 float tmaxfdCer = 0.1; // .10000E+01; // Degree
334 float stemaxCer = .10000E+01; // cm
335 float deemaxCer = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
336 float epsilCer = 1.0E-4; // .10000E+01;
337 float stminCer = 0.0; // cm "Default value used"
338
339 // AIR
340 float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
341 float zAir[4] = {6., 7., 8., 18.};
342 float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
343 float dAir = 1.20479E-3;
344
345 // Carbon fiber
346 float aCf[2] = {12.0107, 1.00794};
347 float zCf[2] = {6., 1.};
348
349 o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir);
350 o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir);
351
352 o2::base::Detector::Material(3, "SILICON$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03);
353 o2::base::Detector::Medium(3, "SILICON$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi);
354}
355
356void Detector::createGeometry()
357{
358 TGeoManager* geoManager = gGeoManager;
359 TGeoVolume* vALIC = geoManager->GetVolume("barrel");
360 if (!vALIC) {
361 LOGP(fatal, "Could not find barrel volume while constructing TRK geometry");
362 }
363 new TGeoVolumeAssembly(GeometryTGeo::getTRKVolPattern());
364 TGeoVolume* vTRK = geoManager->GetVolume(GeometryTGeo::getTRKVolPattern());
365 vALIC->AddNode(vTRK, 2, new TGeoTranslation(0, 30., 0));
366
367 char vstrng[100] = "TRKVol";
368 vTRK->SetTitle(vstrng);
369
370 for (auto& layer : mLayers) {
371 layer->createLayer(vTRK);
372 }
373
374 // Add service for inner tracker
375 mServices.createServices(vTRK);
376
377 // Build the VD using the petal builder
378 // Choose the VD design based on TRKBaseParam.layoutVD
379 auto& trkPars = TRKBaseParam::Instance();
380
382
383 switch (trkPars.layoutVD) {
384 case kIRIS4:
385 LOG(info) << "Building VD with IRIS4 layout";
387 break;
388 case kIRISFullCyl:
389 LOG(info) << "Building VD with IRIS fully cylindrical layout";
391 break;
393 LOG(info) << "Building VD with IRIS fully cylindrical layout with 3 inclined walls";
395 break;
396 case kIRIS5:
397 LOG(info) << "Building VD with IRIS5 layout";
399 break;
400 case kIRIS4a:
401 LOG(info) << "Building VD with IRIS4a layout";
403 break;
404 default:
405 LOG(fatal) << "Unknown VD layout option: " << static_cast<int>(trkPars.layoutVD);
406 break;
407 }
408
409 // Fill sensor names from registry right after geometry creation
410 const auto& regs = o2::trk::vdSensorRegistry();
411 mNumberOfVolumesVD = static_cast<int>(regs.size());
412 mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size();
413 mSensorName.resize(mNumberOfVolumes);
414
415 // Fill VD sensor names from registry
416 int VDvolume = 0;
417 for (const auto& sensor : regs) {
418 mSensorName[VDvolume] = sensor.name;
419 VDvolume++;
420 }
421
422 // Add MLOT sensor names
423 for (int i = 0; i < mLayers.size(); i++) {
424 mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i);
425 }
426
427 for (auto vd : mSensorName) {
428 std::cout << "Volume name: " << vd << std::endl;
429 }
430
431 mServices.excavateFromVacuum("IRIS_CUTOUTsh");
432 mServices.registerVacuum(vTRK);
433
434 // Place forward tracking discs
435
436 TGeoVolume* A3IPvac = gGeoManager->GetVolume("OUT_PIPEVACUUM");
437 if (!A3IPvac) {
438 LOG(info) << "Running simulation with no beam pipe.";
439 }
440
441 // TODO: disambiquate layer/disk below
442 // This will need to adapt to the new scheme
443 if (!A3IPvac) {
444 for (int direction : {kBackward, kForward}) { // Backward layers at mLayers[0]; Forward layers at mLayers[1]
445 const std::string directionString = direction ? "Forward" : "Backward";
446 LOG(info) << " Creating FT3 without beampipe " << directionString << " layers:";
447 for (int iLayer = 0; iLayer < mFT3Layers[direction].size(); iLayer++) {
448 mFT3Layers[direction][iLayer].createLayer(vTRK);
449 }
450 }
451 } else { // If beampipe is enabled append inner disks to beampipe filling volume, this should be temporary.
452 TGeoVolume* volIFT3 = new TGeoVolumeAssembly(GeometryTGeo::getFT3InnerVolPattern());
453 for (int direction : {kBackward, kForward}) {
454 const std::string directionString = direction ? "Forward" : "Backward";
455 LOG(info) << " Creating FT3 " << directionString << " layers:";
456 for (int iLayer = 0; iLayer < mFT3Layers[direction].size(); iLayer++) {
457 LOG(info) << " Creating " << directionString << " layer " << iLayer;
458 if (mFT3Layers[direction][iLayer].getIsInMiddleLayer()) { // ML disks
459 mFT3Layers[direction][iLayer].createLayer(volIFT3);
460 } else {
461 mFT3Layers[direction][iLayer].createLayer(vTRK);
462 }
463 }
464 }
465 A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.));
466 }
467}
468
470{
471 LOG(info) << "Initialize TRK O2Detector";
472 mGeometryTGeo = GeometryTGeo::Instance();
473 defineSensitiveVolumes();
474
475 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
476 for (int i = 0; i < mNumberOfVolumes; i++) {
477 mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry
478 LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data());
479 }
480}
481
482void Detector::defineSensitiveVolumes()
483{
484 TGeoManager* geoManager = gGeoManager;
485 TGeoVolume* v;
486
487 TString volumeName;
488 LOGP(info, "Adding TRK Sensitive Volumes");
489
490 // Register VD sensors created by VDGeometryBuilder
491 for (const auto& s : o2::trk::vdSensorRegistry()) {
492 TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str());
493 if (!v) {
494 LOGP(warning, "VD sensor volume '{}' not found", s.name);
495 continue;
496 }
497 LOGP(info, "Adding VD Sensitive Volume {}", v->GetName());
498 AddSensitiveVolume(v);
499 // Optionally track first/last layers for TR references:
500 if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) {
501 mFirstOrLastLayers.push_back(s.name);
502 }
503 }
504
505 // The names of the TRK sensitive volumes have the format: TRKLayer(0...mLayers.size()-1)
506 for (int j{0}; j < mLayers.size(); j++) {
507 volumeName = GeometryTGeo::getTRKSensorPattern() + TString::Itoa(j, 10);
508 if (j == mLayers.size() - 1) {
509 mFirstOrLastLayers.push_back(volumeName.Data());
510 }
511 LOGP(info, "Trying {}", volumeName.Data());
512 v = geoManager->GetVolume(volumeName.Data());
513 LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName());
514 AddSensitiveVolume(v);
515 }
516
517 // Add FT3 sensitive volumes
518 // TODO: do we need to loop over all volumes in our code, or can we use the geomanager?
519 // Get the flat list of ALL volumes present in the geometry
520 TObjArray* allVolumes = geoManager->GetListOfVolumes();
521 int nVolumes = allVolumes->GetEntriesFast();
522
523 LOG(info) << "Adding FT3 Sensitive Volumes by iterating over all geometry volumes...";
524
525 for (int direction : {kBackward, kForward}) {
526 for (int iLayer = 0; iLayer < getNumberOfFT3Layers(); iLayer++) {
527 int iSens = 0;
528
529 // Build the "signatures" (prefixes) of the names for the various layouts for this specific layer and direction:
530
531 // 1. Trapezoidal/Cylindrical (format: FT3Sensor_<dir>_<layer>)
532 std::string sig1 = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer);
533
534 // 2. Segmented front/back (format: FT3Sensor_front_<layer>_<dir>_...)
535 std::string sig2 = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction);
536 std::string sig3 = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction);
537
538 // 3. SegmentedStave (format: FT3Sensor_<dir>_<layer>_...)
539 // Add the trailing underscore to avoid confusing it with sig1
540 std::string sig4 = "FT3Sensor_Active_" + std::to_string(direction) + "_" + std::to_string(iLayer) + "_";
541
542 // Iterate over all existing volumes to find matches
543 for (int i = 0; i < nVolumes; ++i) {
544 TGeoVolume* v = (TGeoVolume*)allVolumes->At(i);
545 std::string vName = v->GetName();
546
547 // Explicitly exclude the inactive silicon regions created in FT3Module
548 if (vName.find("Inactive") != std::string::npos || vName.find("inactive") != std::string::npos) {
549 continue;
550 }
551
552 // Check if the volume name matches one of our active sensors
553 bool isMatch = false;
554 if (vName == sig1) {
555 isMatch = true; // Exact match for Trapezoidal/Cylindrical layouts
556 } else if (vName.find(sig2) == 0 || vName.find(sig3) == 0 || vName.find(sig4) == 0) {
557 isMatch = true; // Prefix match for Segmented and SegmentedStave layouts
558 }
559
560 if (isMatch) {
561 AddSensitiveVolume(v);
562 /*
563 int volID = gMC ? TVirtualMC::GetMC()->VolId(vName.c_str()) : 0;
564 if (volID > 0) {
565 mActiveSensorMap[volID] = iLayer;
566 }
567 */
568 iSens++;
569 }
570 }
571
572 if (iSens == 0) {
573 LOG(error) << "NO sensitive volume found for FT3 direction " << direction << ", layer " << iLayer;
574 } else {
575 LOG(info) << iSens << " sensitive volume(s) added for FT3 direction " << direction << " layer " << iLayer;
576 }
577 }
578 }
579}
580
581void Detector::EndOfEvent() { Reset(); }
582
583void Detector::Register()
584{
585 // This will create a branch in the output tree called Hit, setting the last
586 // parameter to kFALSE means that this collection will not be written to the file,
587 // it will exist only during the simulation
588
589 if (FairRootManager::Instance()) {
590 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true);
591 }
592}
593
594void Detector::Reset()
595{
596 if (!o2::utils::ShmManager::Instance().isOperational()) {
597 mHits->clear();
598 }
599}
600
601bool Detector::InsideFirstOrLastLayer(std::string layerName)
602{
603 bool inside = false;
604 for (auto& firstOrLastLayer : mFirstOrLastLayers) {
605 if (firstOrLastLayer == layerName) {
606 inside = true;
607 break;
608 }
609 }
610 return inside;
611}
612
613bool Detector::ProcessHits(FairVolume* vol)
614{
615 // This method is called from the MC stepping
616 if (!(fMC->TrackCharge())) {
617 return false;
618 }
619
620 int subDetID = -1;
621 int layer = -1;
622 int volume = 0;
623 int volID = vol->getMCid();
624
625 bool notSens = false;
626 while ((volume < mNumberOfVolumes) && (notSens = (volID != mSensorID[volume]))) {
627 ++volume;
628 }
629
630 if (volume < mNumberOfVolumesVD) {
631 subDetID = 0; // VD. For the moment each "chip" is a volume./// TODO: change this logic once the naming scheme is changed
632 } else {
633 subDetID = 1; // MLOT
634 layer = volume - mNumberOfVolumesVD;
635 }
636
637 if (strstr(vol->GetName(), "FT3Sensor_Active") || strstr(vol->GetName(), "FT3Chip")) {
638 subDetID = 2;
639 notSens = false;
640 }
641
642 // TODO: add corresponding logic for disks. I think Ruben is right; this is only called for active volumes!
643 if (notSens) {
644 LOG(info) << "ProcessHit called for insensitive volume " << vol->GetName();
645 return kFALSE; // RS: can this happen? This method must be called for sensors only?
646 }
647
648 // Is it needed to keep a track reference when the outer ITS volume is encountered?
649 auto stack = (o2::data::Stack*)fMC->GetStack();
650 // if (fMC->IsTrackExiting() && (lay == 0 || lay == mLayers.size() - 1)) {
651 if (fMC->IsTrackExiting() && subDetID < 2 && InsideFirstOrLastLayer(vol->GetName())) {
652 // Keep the track refs for the innermost and outermost layers only
653 o2::TrackReference tr(*fMC, GetDetId());
654 tr.setTrackID(stack->GetCurrentTrackNumber());
655 tr.setUserId(volume);
656 stack->addTrackReference(tr);
657 }
658 bool startHit = false, stopHit = false;
659 unsigned char status = 0;
660 if (fMC->IsTrackEntering()) {
661 status |= Hit::kTrackEntering;
662 }
663 if (fMC->IsTrackInside()) {
664 status |= Hit::kTrackInside;
665 }
666 if (fMC->IsTrackExiting()) {
667 status |= Hit::kTrackExiting;
668 }
669 if (fMC->IsTrackOut()) {
670 status |= Hit::kTrackOut;
671 }
672 if (fMC->IsTrackStop()) {
673 status |= Hit::kTrackStopped;
674 }
675 if (fMC->IsTrackAlive()) {
676 status |= Hit::kTrackAlive;
677 }
678
679 // track is entering or created in the volume
680 if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) {
681 startHit = true;
682 } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) {
683 stopHit = true;
684 }
685
686 // increment energy loss at all steps except entrance
687 if (!startHit) {
688 mTrackData.mEnergyLoss += fMC->Edep();
689 }
690 if (!(startHit | stopHit)) {
691 return false; // do noting
692 }
693
694 if (startHit) {
695 mTrackData.mEnergyLoss = 0.;
696 fMC->TrackMomentum(mTrackData.mMomentumStart);
697 fMC->TrackPosition(mTrackData.mPositionStart);
698 mTrackData.mTrkStatusStart = status;
699 mTrackData.mHitStarted = true;
700 }
701 if (stopHit) {
702 TLorentzVector positionStop;
703 fMC->TrackPosition(positionStop);
704
705 // Retrieve the indices with the volume path
706 int stave(0), halfstave(0), mod(0), chip(0);
707
708 auto& trkPars = TRKBaseParam::Instance();
709
710 if (subDetID == 1) {
711 if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kSegmented) {
712 fMC->CurrentVolOffID(1, chip);
713 fMC->CurrentVolOffID(2, mod);
714 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) {
715 fMC->CurrentVolOffID(3, halfstave);
716 fMC->CurrentVolOffID(4, stave);
717 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) {
718 fMC->CurrentVolOffID(3, stave);
719 } else {
720 LOGP(fatal, "Wrong number of halfstaves for layer {}", layer);
721 }
722 }
723 }
724 else if (subDetID == 2) {
725 mGeometryTGeo->extractChipIdsFT3(vol->GetName(), layer, stave, chip);
726 }
727 unsigned short chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave, mod, chip);
728
729 // Print(vol, volume, subDetID, layer, stave, halfstave, mod, chip, chipID);
730
731 // mGeometryTGeo->Print();
732
733 Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
734 mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
735 mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
736 // p->SetTotalEnergy(vmc->Etot());
737
738 // RS: not sure this is needed
739 // Increment number of Detector det points in TParticle
740 stack->addHit(GetDetId());
741 }
742
743 return true;
744}
745
746o2::trk::Hit* Detector::addHit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos,
747 const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus,
748 unsigned char endStatus)
749{
750 mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus);
751 return &(mHits->back());
752}
753
754void Detector::Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const
755{
756 int currentVol(0);
757 LOG(info) << "Current volume name: " << fMC->CurrentVolName() << " and ID " << fMC->CurrentVolID(currentVol);
758 LOG(info) << "volume: " << volume << "/" << mNumberOfVolumes - 1;
759
760 auto& trkPars = TRKBaseParam::Instance();
761
762 if (subDetID == 1) { // MLOT
763 if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kCylindrical) {
764 LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip;
765 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " Chip ID: " << chipID;
766 } else {
767 LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip;
768 LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod;
769 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry
770 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave;
771 LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave;
772 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
773 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry
774 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave;
775 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
776 }
777 }
778 } else {
779 // VD
780 LOG(info) << "SubDetector ID: " << subDetID << " Chip ID: " << chipID;
781 }
782
783 LOG(info);
784}
785
786} // namespace trk
787} // namespace o2
788
790
791// Define Factory method for calling from the outside
792extern "C" {
797}
Definition of the Stack class.
Definition of the TRK Hit class.
int32_t i
uint32_t j
Definition RawData.h:0
uint32_t stack
Definition RawData.h:1
specs of the ALICE3 TRK
ClassImp(o2::trk::Detector)
o2::base::Detector * create_detector_trk(bool active)
Definition Detector.cxx:793
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
virtual void InitializeO2Detector()=0
Definition Detector.cxx:98
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:150
void configFromFile(std::string fileName="alice3_TRK_layout.txt")
Definition Detector.cxx:188
static o2::base::Detector * create(bool active)
Definition Detector.h:40
void configToFile(std::string fileName="alice3_TRK_layout.txt")
Definition Detector.cxx:300
void configFT3ScopingV3()
Definition Detector.cxx:138
static ShmManager & Instance()
Definition ShmManager.h:61
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition glcorearb.h:2513
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLuint GLfloat x0
Definition glcorearb.h:5034
GLuint GLfloat * val
Definition glcorearb.h:1582
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
void createGeometry(TGeoManager &geom, TGeoVolume &topVolume)
Definition Geometry.cxx:74
void createMaterials()
Definition Materials.cxx:91
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:28
void createIRIS4Geometry(TGeoVolume *motherVolume)
void createIRIS5Geometry(TGeoVolume *motherVolume)
@ kIRISFullCyl3InclinedWalls
void clearVDSensorRegistry()
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"