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
18#include "DetectorsBase/Stack.h"
19#include "TRKSimulation/Hit.h"
24
25#include <string>
26#include <type_traits>
27
28using o2::trk::Hit;
29
30namespace o2
31{
32namespace trk
33{
34
35float getDetLengthFromEta(const float eta, const float radius)
36{
37 return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta))));
38}
39
41 : o2::base::DetImpl<Detector>("TRK", true),
42 mTrackData(),
43 mHits(o2::utils::createSimVector<o2::trk::Hit>())
44{
45}
46
47Detector::Detector(bool active)
48 : o2::base::DetImpl<Detector>("TRK", true),
49 mTrackData(),
50 mHits(o2::utils::createSimVector<o2::trk::Hit>())
51{
52 auto& trkPars = TRKBaseParam::Instance();
53
54 if (trkPars.configFile != "") {
55 configFromFile(trkPars.configFile);
56 } else {
57 configMLOT();
58 configToFile();
59 configServices();
60 }
61
62 LOGP(info, "Summary of TRK configuration:");
63 for (auto& layer : mLayers) {
64 LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer->getNumber(), layer->getName(), layer->getInnerRadius(), layer->getZ(), layer->getChipThickness());
65 }
66}
67
68Detector::Detector(const Detector& other)
69 : o2::base::DetImpl<Detector>(other),
70 mTrackData(),
71 mHits(o2::utils::createSimVector<o2::trk::Hit>())
72{
73}
74
75Detector::~Detector()
76{
77 if (mHits) {
79 }
80}
81
82void Detector::ConstructGeometry()
83{
86}
87
88void Detector::configMLOT()
89{
90 auto& trkPars = TRKBaseParam::Instance();
91
92 mLayers.clear();
93
94 const std::vector<float> rInn{7.f, 9.f, 12.f, 20.f, 30.f, 45.f, 60.f, 80.f};
95 const float thick = 100.e-3;
96
97 switch (trkPars.layoutMLOT) {
98 case kCylindrical: {
99 const std::vector<float> length{128.35f, 128.35f, 128.35f, 128.35f, 128.35f, 256.7f, 256.7f, 256.7f};
100 LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK");
101 for (int i{0}; i < 8; ++i) {
102 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i);
103 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(i, name, rInn[i], length[i], thick, MatBudgetParamMode::Thickness));
104 }
105 break;
106 }
107 case kSegmented: {
108 const std::vector<int> nMods{10, 10, 10, 10, 10, 20, 20, 20};
109 LOGP(warning, "Loading segmented configuration for ALICE3 TRK");
110 for (int i{0}; i < 8; ++i) {
111 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i);
112 if (i < 4) {
113 mLayers.push_back(std::make_unique<TRKMLLayer>(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness));
114 } else {
115 mLayers.push_back(std::make_unique<TRKOTLayer>(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness));
116 }
117 }
118 break;
119 }
120 default:
121 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
122 break;
123 }
124}
125
126void Detector::configFromFile(std::string fileName)
127{
128 // Override the default geometry if config file provided
129 std::ifstream confFile(fileName);
130 if (!confFile.good()) {
131 LOGP(fatal, "File {} not found, aborting.", fileName);
132 }
133
134 auto& trkPars = TRKBaseParam::Instance();
135
136 mLayers.clear();
137
138 LOGP(info, "Overriding geometry of ALICE3 TRK using {} file.", fileName);
139
140 std::string line;
141 std::vector<float> tmpBuff;
142 int layerCount{0};
143 while (std::getline(confFile, line)) {
144 if (line[0] == '/') {
145 continue;
146 }
147 tmpBuff.clear();
148 std::stringstream ss(line);
149 float val;
150 std::string substr;
151 while (getline(ss, substr, '\t')) {
152 tmpBuff.push_back(std::stof(substr));
153 }
154
155 std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount);
156 switch (trkPars.layoutMLOT) {
157 case kCylindrical:
158 mLayers.push_back(std::make_unique<TRKCylindricalLayer>(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness));
159 break;
160 case kSegmented: {
161 int nMods = static_cast<int>(tmpBuff[1]);
162 if (layerCount < 4) {
163 mLayers.push_back(std::make_unique<TRKMLLayer>(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness));
164 } else {
165 mLayers.push_back(std::make_unique<TRKOTLayer>(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness));
166 }
167 break;
168 }
169 default:
170 LOGP(fatal, "Unknown option {} for configMLOT", static_cast<int>(trkPars.layoutMLOT));
171 break;
172 }
173
174 ++layerCount;
175 }
176}
177
178void Detector::configToFile(std::string fileName)
179{
180 LOGP(info, "Exporting TRK Detector layout to {}", fileName);
181 std::ofstream conFile(fileName.c_str(), std::ios::out);
182 conFile << "/// TRK configuration file: inn_radius z_length lay_thickness" << std::endl;
183 for (const auto& layer : mLayers) {
184 conFile << layer->getInnerRadius() << "\t" << layer->getZ() << "\t" << layer->getChipThickness() << std::endl;
185 }
186}
187
188void Detector::configServices()
189{
190 mServices = TRKServices();
191}
192
193void Detector::createMaterials()
194{
195 int ifield = 2; // ?
196 float fieldm = 10.0; // ?
198
199 float tmaxfdSi = 0.1; // .10000E+01; // Degree
200 float stemaxSi = 0.0075; // .10000E+01; // cm
201 float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
202 float epsilSi = 1.0E-4; // .10000E+01;
203 float stminSi = 0.0; // cm "Default value used"
204
205 float tmaxfdAir = 0.1; // .10000E+01; // Degree
206 float stemaxAir = .10000E+01; // cm
207 float deemaxAir = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
208 float epsilAir = 1.0E-4; // .10000E+01;
209 float stminAir = 0.0; // cm "Default value used"
210
211 float tmaxfdCer = 0.1; // .10000E+01; // Degree
212 float stemaxCer = .10000E+01; // cm
213 float deemaxCer = 0.1; // 0.30000E-02; // Fraction of particle's energy 0<deemax<=1
214 float epsilCer = 1.0E-4; // .10000E+01;
215 float stminCer = 0.0; // cm "Default value used"
216
217 // AIR
218 float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
219 float zAir[4] = {6., 7., 8., 18.};
220 float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
221 float dAir = 1.20479E-3;
222
223 // Carbon fiber
224 float aCf[2] = {12.0107, 1.00794};
225 float zCf[2] = {6., 1.};
226
227 o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir);
228 o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir);
229
230 o2::base::Detector::Material(3, "SILICON$", 0.28086E+02, 0.14000E+02, 0.23300E+01, 0.93600E+01, 0.99900E+03);
231 o2::base::Detector::Medium(3, "SILICON$", 3, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi);
232}
233
234void Detector::createGeometry()
235{
236 TGeoManager* geoManager = gGeoManager;
237 TGeoVolume* vALIC = geoManager->GetVolume("barrel");
238 if (!vALIC) {
239 LOGP(fatal, "Could not find barrel volume while constructing TRK geometry");
240 }
241 new TGeoVolumeAssembly(GeometryTGeo::getTRKVolPattern());
242 TGeoVolume* vTRK = geoManager->GetVolume(GeometryTGeo::getTRKVolPattern());
243 vALIC->AddNode(vTRK, 2, new TGeoTranslation(0, 30., 0));
244
245 char vstrng[100] = "TRKVol";
246 vTRK->SetTitle(vstrng);
247
248 for (auto& layer : mLayers) {
249 layer->createLayer(vTRK);
250 }
251
252 // Add service for inner tracker
253 mServices.createServices(vTRK);
254
255 // Build the VD using the petal builder
256 // Choose the VD design based on TRKBaseParam.layoutVD
257 auto& trkPars = TRKBaseParam::Instance();
258
260
261 switch (trkPars.layoutVD) {
262 case kIRIS4:
263 LOG(info) << "Building VD with IRIS4 layout";
265 break;
266 case kIRISFullCyl:
267 LOG(info) << "Building VD with IRIS fully cylindrical layout";
269 break;
271 LOG(info) << "Building VD with IRIS fully cylindrical layout with 3 inclined walls";
273 break;
274 case kIRIS5:
275 LOG(info) << "Building VD with IRIS5 layout";
277 break;
278 case kIRIS4a:
279 LOG(info) << "Building VD with IRIS4a layout";
281 break;
282 default:
283 LOG(fatal) << "Unknown VD layout option: " << static_cast<int>(trkPars.layoutVD);
284 break;
285 }
286
287 // Fill sensor names from registry right after geometry creation
288 const auto& regs = o2::trk::vdSensorRegistry();
289 mNumberOfVolumesVD = static_cast<int>(regs.size());
290 mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size();
291 mSensorName.resize(mNumberOfVolumes);
292
293 // Fill VD sensor names from registry
294 int VDvolume = 0;
295 for (const auto& sensor : regs) {
296 mSensorName[VDvolume] = sensor.name;
297 VDvolume++;
298 }
299
300 // Add MLOT sensor names
301 for (int i = 0; i < mLayers.size(); i++) {
302 mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i);
303 }
304
305 for (auto vd : mSensorName) {
306 std::cout << "Volume name: " << vd << std::endl;
307 }
308
309 mServices.excavateFromVacuum("IRIS_CUTOUTsh");
310 mServices.registerVacuum(vTRK);
311}
312
313void Detector::InitializeO2Detector()
314{
315 LOG(info) << "Initialize TRK O2Detector";
316 mGeometryTGeo = GeometryTGeo::Instance();
317 defineSensitiveVolumes();
318
319 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
320 for (int i = 0; i < mNumberOfVolumes; i++) {
321 mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry
322 LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data());
323 }
324}
325
326void Detector::defineSensitiveVolumes()
327{
328 TGeoManager* geoManager = gGeoManager;
329 TGeoVolume* v;
330
331 TString volumeName;
332 LOGP(info, "Adding TRK Sensitive Volumes");
333
334 // Register VD sensors created by VDGeometryBuilder
335 for (const auto& s : o2::trk::vdSensorRegistry()) {
336 TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str());
337 if (!v) {
338 LOGP(warning, "VD sensor volume '{}' not found", s.name);
339 continue;
340 }
341 LOGP(info, "Adding VD Sensitive Volume {}", v->GetName());
342 AddSensitiveVolume(v);
343 // Optionally track first/last layers for TR references:
344 if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) {
345 mFirstOrLastLayers.push_back(s.name);
346 }
347 }
348
349 // The names of the TRK sensitive volumes have the format: TRKLayer(0...mLayers.size()-1)
350 for (int j{0}; j < mLayers.size(); j++) {
351 volumeName = GeometryTGeo::getTRKSensorPattern() + TString::Itoa(j, 10);
352 if (j == mLayers.size() - 1) {
353 mFirstOrLastLayers.push_back(volumeName.Data());
354 }
355 LOGP(info, "Trying {}", volumeName.Data());
356 v = geoManager->GetVolume(volumeName.Data());
357 LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName());
358 AddSensitiveVolume(v);
359 }
360}
361
362void Detector::EndOfEvent() { Reset(); }
363
364void Detector::Register()
365{
366 // This will create a branch in the output tree called Hit, setting the last
367 // parameter to kFALSE means that this collection will not be written to the file,
368 // it will exist only during the simulation
369
370 if (FairRootManager::Instance()) {
371 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true);
372 }
373}
374
375void Detector::Reset()
376{
377 if (!o2::utils::ShmManager::Instance().isOperational()) {
378 mHits->clear();
379 }
380}
381
382bool Detector::InsideFirstOrLastLayer(std::string layerName)
383{
384 bool inside = false;
385 for (auto& firstOrLastLayer : mFirstOrLastLayers) {
386 if (firstOrLastLayer == layerName) {
387 inside = true;
388 break;
389 }
390 }
391 return inside;
392}
393
394bool Detector::ProcessHits(FairVolume* vol)
395{
396 // This method is called from the MC stepping
397 if (!(fMC->TrackCharge())) {
398 return false;
399 }
400
401 int subDetID = -1;
402 int layer = -1;
403 int volume = 0;
404 int volID = vol->getMCid();
405
406 bool notSens = false;
407 while ((volume < mNumberOfVolumes) && (notSens = (volID != mSensorID[volume]))) {
408 ++volume;
409 }
410
411 if (notSens) {
412 return kFALSE; // RS: can this happen? This method must be called for sensors only?
413 }
414
415 if (volume < mNumberOfVolumesVD) {
416 subDetID = 0; // VD. For the moment each "chip" is a volume./// TODO: change this logic once the naming scheme is changed
417 } else {
418 subDetID = 1; // MLOT
419 layer = volume - mNumberOfVolumesVD;
420 }
421
422 // Is it needed to keep a track reference when the outer ITS volume is encountered?
423 auto stack = (o2::data::Stack*)fMC->GetStack();
424 // if (fMC->IsTrackExiting() && (lay == 0 || lay == mLayers.size() - 1)) {
425 if (fMC->IsTrackExiting() && InsideFirstOrLastLayer(vol->GetName())) {
426 // Keep the track refs for the innermost and outermost layers only
427 o2::TrackReference tr(*fMC, GetDetId());
428 tr.setTrackID(stack->GetCurrentTrackNumber());
429 tr.setUserId(volume);
430 stack->addTrackReference(tr);
431 }
432 bool startHit = false, stopHit = false;
433 unsigned char status = 0;
434 if (fMC->IsTrackEntering()) {
435 status |= Hit::kTrackEntering;
436 }
437 if (fMC->IsTrackInside()) {
438 status |= Hit::kTrackInside;
439 }
440 if (fMC->IsTrackExiting()) {
441 status |= Hit::kTrackExiting;
442 }
443 if (fMC->IsTrackOut()) {
444 status |= Hit::kTrackOut;
445 }
446 if (fMC->IsTrackStop()) {
447 status |= Hit::kTrackStopped;
448 }
449 if (fMC->IsTrackAlive()) {
450 status |= Hit::kTrackAlive;
451 }
452
453 // track is entering or created in the volume
454 if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) {
455 startHit = true;
456 } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) {
457 stopHit = true;
458 }
459
460 // increment energy loss at all steps except entrance
461 if (!startHit) {
462 mTrackData.mEnergyLoss += fMC->Edep();
463 }
464 if (!(startHit | stopHit)) {
465 return false; // do noting
466 }
467
468 if (startHit) {
469 mTrackData.mEnergyLoss = 0.;
470 fMC->TrackMomentum(mTrackData.mMomentumStart);
471 fMC->TrackPosition(mTrackData.mPositionStart);
472 mTrackData.mTrkStatusStart = status;
473 mTrackData.mHitStarted = true;
474 }
475 if (stopHit) {
476 TLorentzVector positionStop;
477 fMC->TrackPosition(positionStop);
478 // Retrieve the indices with the volume path
479 int stave(0), halfstave(0), mod(0), chip(0);
480 if (subDetID == 1) {
481 fMC->CurrentVolOffID(1, chip);
482 fMC->CurrentVolOffID(2, mod);
483 if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) {
484 fMC->CurrentVolOffID(3, halfstave);
485 fMC->CurrentVolOffID(4, stave);
486 } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) {
487 fMC->CurrentVolOffID(3, stave);
488 } else {
489 LOGP(fatal, "Wrong number of halfstaves for layer {}", layer);
490 }
491 }
492
493 unsigned short chipID = mGeometryTGeo->getChipIndex(subDetID, volume, layer, stave, halfstave, mod, chip);
494
495 // Print(vol, volume, subDetID, layer, stave, halfstave, mod, chip, chipID);
496
497 // mGeometryTGeo->Print();
498
499 Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(),
500 mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(),
501 mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status);
502 // p->SetTotalEnergy(vmc->Etot());
503
504 // RS: not sure this is needed
505 // Increment number of Detector det points in TParticle
506 stack->addHit(GetDetId());
507 }
508
509 return true;
510}
511
512o2::trk::Hit* Detector::addHit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos,
513 const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus,
514 unsigned char endStatus)
515{
516 mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus);
517 return &(mHits->back());
518}
519
520void Detector::Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int mod, int chip, int chipID) const
521{
522 int currentVol(0);
523 LOG(info) << "Current volume name: " << fMC->CurrentVolName() << " and ID " << fMC->CurrentVolID(currentVol);
524 LOG(info) << "volume: " << volume << "/" << mNumberOfVolumes - 1;
525 LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip;
526 LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod;
527 if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry
528 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave;
529 LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave;
530 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
531 } else if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry
532 LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave;
533 LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID;
534 } else {
535 LOG(info) << "SubDetector ID: " << subDetID << " Chip ID: " << chipID;
536 }
537 LOG(info);
538}
539
540} // namespace trk
541} // namespace o2
542
544
545// Define Factory method for calling from the outside
546extern "C" {
551}
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
ClassImp(o2::trk::Detector)
o2::base::Detector * create_detector_trk(bool active)
Definition Detector.cxx:547
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
static o2::base::Detector * create(bool active)
Definition Detector.h:39
static ShmManager & Instance()
Definition ShmManager.h:61
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
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:35
void createIRISGeometry3InclinedWalls(TGeoVolume *motherVolume)
void createIRISGeometryFullCyl(TGeoVolume *motherVolume)
void createIRIS4aGeometry(TGeoVolume *motherVolume)
void createIRIS4Geometry(TGeoVolume *motherVolume)
void createIRIS5Geometry(TGeoVolume *motherVolume)
@ kIRISFullCyl3InclinedWalls
void clearVDSensorRegistry()
void freeSimVector(std::vector< T > *ptr)
std::vector< T > * createSimVector()
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"