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 <Buttons.h>
13#include <TGeoCompositeShape.h>
14#include <TGeoShape.h>
15#include <TGeoBBox.h>
16#include <TGeoTube.h>
17#include <TGeoSphere.h>
18#include <TGeoVolume.h>
19#include <TMCManagerStack.h>
20#include "TGeoManager.h" // for TGeoManager
21#include "TMath.h"
22#include "TGraph.h"
23#include "TString.h"
24#include "TSystem.h"
25#include "TVirtualMC.h"
26#include "TVector3.h"
27
28#include "FairRootManager.h" // for FairRootManager
29#include <fairlogger/Logger.h>
30#include "FairVolume.h"
31
32#include "FairRootManager.h"
33#include "FairVolume.h"
34
35#include <sstream>
36#include <string>
37#include "FT0Base/Geometry.h"
39#include "DetectorsBase/Stack.h"
40
41using namespace o2::ft0;
43
45
46Detector::Detector(Bool_t Active)
47 : o2::base::DetImpl<Detector>("FT0", Active), mIdSens1(0), mPMTeff(nullptr), mHits(o2::utils::createSimVector<o2::ft0::HitType>()), mTrackIdTop(-1), mTrackIdMCPtop(-1)
48
49{
50 // Gegeo = GetGeometry() ;
51 // TString gn(geo->GetName());
52}
53
55 : o2::base::DetImpl<Detector>(rhs), mIdSens1(rhs.mIdSens1), mPMTeff(rhs.mPMTeff), mHits(o2::utils::createSimVector<o2::ft0::HitType>()), mTrackIdTop(-1), mTrackIdMCPtop(-1)
56{
57}
58
63
65{
66 // FIXME: we need to register the sensitive volumes with FairRoot
67 TVirtualMC* fMC = TVirtualMC::GetMC();
68 TGeoVolume* v = gGeoManager->GetVolume("0REG");
69 if (v == nullptr) {
70 LOG(warn) << "@@@@ Sensitive volume 0REG not found!!!!!!!!";
71 } else {
72 AddSensitiveVolume(v);
73 mREGVolID = fMC->VolId("0REG");
74 }
75 TGeoVolume* vrad = gGeoManager->GetVolume("0TOP");
76 if (vrad == nullptr) {
77 LOG(warn) << "@@@@ Sensitive radiator not found!!!!!!!!";
78 } else {
79 AddSensitiveVolume(vrad);
80 mTOPVolID = fMC->VolId("0TOP");
81 }
82 TGeoVolume* vmcp = gGeoManager->GetVolume("0MTO");
83 if (vmcp == nullptr) {
84 LOG(warn) << "@@@@ Sensitive MCP glass not found!!!!!!!!";
85 } else {
86 AddSensitiveVolume(vmcp);
87 mMTOVolID = fMC->VolId("0MTO");
88 }
89}
90
92{
93 LOG(debug) << "Creating FT0 geometry\n";
95
96 TGeoVolumeAssembly* stlinA = new TGeoVolumeAssembly("FT0A"); // A side mother
97 TGeoVolumeAssembly* stlinC = new TGeoVolumeAssembly("FT0C"); // C side mother
98
99 Geometry geometry;
100 Float_t zdetA = geometry.ZdetA;
101 Float_t zdetC = geometry.ZdetC;
102 int nCellsA = geometry.NCellsA;
103 int nCellsC = geometry.NCellsC;
104
105 for (int ipos = 0; ipos < nCellsA; ipos++) {
106 mPosModuleAx[ipos] = geometry.centerMCP(ipos).X();
107 mPosModuleAy[ipos] = geometry.centerMCP(ipos).Y();
108 }
109
110 // FIT interior
111 TVirtualMC::GetMC()->Gsvolu("0MOD", "BOX", getMediumID(kAir), mInStart, 3);
112 TGeoVolume* ins = gGeoManager->GetVolume("0MOD");
113 //
114 TGeoTranslation* tr[nCellsA + nCellsC];
115 TString nameTr;
116 // A side Translations
117 for (Int_t itr = 0; itr < Geometry::NCellsA; itr++) {
118 nameTr = Form("0TR%i", itr + 1);
119 float z = -mStartA[2] + mInStart[2];
120 tr[itr] = new TGeoTranslation(nameTr.Data(), mPosModuleAx[itr], mPosModuleAy[itr], z);
121 tr[itr]->RegisterYourself();
122 stlinA->AddNode(ins, itr, tr[itr]);
123 LOG(debug) << " A geom " << itr << " " << mPosModuleAx[itr] << " " << mPosModuleAy[itr];
124 }
125 SetCablesA(stlinA);
126
127 // Add FT0-A support Structure to the geometry
128 stlinA->AddNode(constructFrameAGeometry(), 1, new TGeoTranslation(0, 0, -mStartA[2] + mInStart[2]));
129
130 // C Side
131 TGeoRotation* rot[nCellsC];
132 TString nameRot;
133 TGeoCombiTrans* com[nCellsC];
134 TGeoCombiTrans* comCable[nCellsC];
135 TString nameCom;
136
137 //Additional elements for the C-side frame
138 TGeoCombiTrans* plateCom[nCellsC];
139 TGeoMedium* Al = gGeoManager->GetMedium("FT0_Aluminium$");
140 TGeoCompositeShape* plateCompositeShape = new TGeoCompositeShape("plateCompositeShape", cPlateShapeString().c_str());
141 TGeoVolume* plateVol = new TGeoVolume("plateVol", plateCompositeShape, Al);
142
143 for (Int_t itr = Geometry::NCellsA; itr < Geometry::NCellsA + nCellsC; itr++) {
144 nameTr = Form("0TR%i", itr + 1);
145 nameRot = Form("0Rot%i", itr + 1);
146 int ic = itr - Geometry::NCellsA;
147 float ac1 = geometry.tiltMCP(ic).X();
148 float bc1 = geometry.tiltMCP(ic).Y();
149 float gc1 = geometry.tiltMCP(ic).Z();
150 rot[ic] = new TGeoRotation(nameRot.Data(), ac1, bc1, gc1);
151 LOG(debug) << " rot geom " << ic << " " << ac1 << " " << bc1 << " " << gc1;
152 rot[ic]->RegisterYourself();
153 mPosModuleCx[ic] = geometry.centerMCP(ic + nCellsA).X();
154 mPosModuleCy[ic] = geometry.centerMCP(ic + nCellsA).Y();
155 mPosModuleCz[ic] = geometry.centerMCP(ic + nCellsA).Z() - 80; // !!! fix later
156 com[ic] = new TGeoCombiTrans(mPosModuleCx[ic], mPosModuleCy[ic], mPosModuleCz[ic], rot[ic]);
157 TGeoHMatrix hm = *com[ic];
158 TGeoHMatrix* ph = new TGeoHMatrix(hm);
159 stlinC->AddNode(ins, itr, ph);
160 // cables
161 TGeoVolume* cables = SetCablesSize(itr);
162 LOG(debug) << " C " << mPosModuleCx[ic] << " " << mPosModuleCy[ic];
163 // cables->Print();
164 //Additional shift (+0.1) introduced to cable planes so they don't overlap the C-side frame
165 comCable[ic] = new TGeoCombiTrans(mPosModuleCx[ic], mPosModuleCy[ic], mPosModuleCz[ic] + mInStart[2] + 0.2 + 0.1, rot[ic]);
166 TGeoHMatrix hmCable = *comCable[ic];
167 TGeoHMatrix* phCable = new TGeoHMatrix(hmCable);
168 stlinC->AddNode(cables, itr, comCable[ic]);
169
170 //C-side frame elements - module plates
171 plateCom[ic] = new TGeoCombiTrans(mPosModuleCx[ic], mPosModuleCy[ic], (mPosModuleCz[ic] - 3), rot[ic]);
172 TGeoHMatrix hmPlate = *plateCom[ic];
173 TGeoHMatrix* phPlate = new TGeoHMatrix(hmPlate);
174 stlinC->AddNode(plateVol, itr, phPlate);
175 }
176 //Add C-side frame
177 stlinC->AddNode(constructFrameCGeometry(), nCellsA + nCellsC + 1);
178
179 TGeoVolume* alice = gGeoManager->GetVolume("barrel");
180 //Add A-side detector
181 alice->AddNode(stlinA, 1, new TGeoTranslation(0, 30., zdetA + 0.63)); //offset to avoid overlap with FV0
182
183 //Add C-side detector
184 TGeoRotation* rotC = new TGeoRotation("rotC", 90., 0., 90., 90., 180., 0.);
185 alice->AddNode(stlinC, 1, new TGeoCombiTrans(0., 30., -zdetC, rotC));
186
187 // MCP + 4 x wrapped radiator + 4xphotocathod + MCP + Al top in front of radiators
188 SetOneMCP(ins);
189 // SetCablesC(stlinC);
190}
191
193{
194 LOG(debug) << "Creating FIT optical geometry properties";
195
198}
199
200//_________________________________________
201void Detector::SetOneMCP(TGeoVolume* ins)
202{
203
204 Double_t x, y, z;
205
206 Float_t ptop[3] = {1.324, 1.324, 1.}; // Cherenkov radiator
207 Float_t ptopref[3] = {1.3241, 1.3241, 1.}; // Cherenkov radiator wrapped with reflector
208 Double_t prfv[3] = {0.0002, 1.323, 1.}; // Vertical refracting layer bettwen radiators and between radiator and not optical Air
209 Double_t prfh[3] = {1.323, 0.0002, 1.}; // Horizontal refracting layer bettwen radiators and ...
210 Float_t pmcp[3] = {2.949, 2.949, 0.66}; // MCP
211 Float_t pmcpinner[3] = {2.749, 2.749, 0.1};
212 Float_t pmcpbase[3] = {2.949, 2.949, 0.675};
213 Float_t pmcpside[3] = {0.15, 2.949, 0.65};
214 Float_t pmcptopglass[3] = {2.949, 2.949, 0.1}; // MCP top glass optical
215 Float_t preg[3] = {1.324, 1.324, 0.005}; // Photcathode
216 // Entry window (glass)
217 TVirtualMC::GetMC()->Gsvolu("0TOP", "BOX", getMediumID(kOpGlass), ptop, 3); // Glass radiator
218 TGeoVolume* top = gGeoManager->GetVolume("0TOP");
219 TVirtualMC::GetMC()->Gsvolu("0TRE", "BOX", getMediumID(kAir), ptopref, 3); // Air: wrapped radiator
220 TGeoVolume* topref = gGeoManager->GetVolume("0TRE");
221 TVirtualMC::GetMC()->Gsvolu("0RFV", "BOX", getMediumID(kOptAl), prfv, 3); // Optical Air vertical
222 TGeoVolume* rfv = gGeoManager->GetVolume("0RFV");
223 TVirtualMC::GetMC()->Gsvolu("0RFH", "BOX", getMediumID(kOptAl), prfh, 3); // Optical Air horizontal
224 TGeoVolume* rfh = gGeoManager->GetVolume("0RFH");
225
226 TVirtualMC::GetMC()->Gsvolu("0REG", "BOX", getMediumID(kOpGlassCathode), preg, 3);
227 TGeoVolume* cat = gGeoManager->GetVolume("0REG");
228
229 // wrapped radiator + reflecting layers
230
231 Int_t ntops = 0, nrfvs = 0, nrfhs = 0;
232 x = y = z = 0;
233 topref->AddNode(top, 1, new TGeoTranslation(0, 0, 0));
234 float xinv = -ptop[0] - prfv[0];
235 topref->AddNode(rfv, 1, new TGeoTranslation(xinv, 0, 0));
236 xinv = ptop[0] + prfv[0];
237 topref->AddNode(rfv, 2, new TGeoTranslation(xinv, 0, 0));
238 float yinv = -ptop[1] - prfh[1];
239 topref->AddNode(rfh, 1, new TGeoTranslation(0, yinv, 0));
240 yinv = ptop[1] + prfh[1];
241 topref->AddNode(rfh, 2, new TGeoTranslation(0, yinv, 0));
242
243 // container for radiator, cathode
244 for (Int_t ix = 0; ix < 2; ix++) {
245 float xin = -mInStart[0] + 0.3 + (ix + 0.5) * 2 * ptopref[0];
246 for (Int_t iy = 0; iy < 2; iy++) {
247 float yin = -mInStart[1] + 0.3 + (iy + 0.5) * 2 * ptopref[1];
248 ntops++;
249 z = -mInStart[2] + ptopref[2];
250 ins->AddNode(topref, ntops, new TGeoTranslation(xin, yin, z));
251 LOG(debug) << " n " << ntops << " x " << xin << " y " << yin << " z radiator " << z;
252 z += ptopref[2] + 2. * pmcptopglass[2] + preg[2];
253 ins->AddNode(cat, ntops, new TGeoTranslation(xin, yin, z));
254 LOG(debug) << " n " << ntops << " x " << xin << " y " << yin << " z cathod " << z;
255 }
256 }
257 // MCP
258 TVirtualMC::GetMC()->Gsvolu("0MTO", "BOX", getMediumID(kOpGlass), pmcptopglass, 3); // Op Glass
259 TGeoVolume* mcptop = gGeoManager->GetVolume("0MTO");
260 z = -mInStart[2] + 2 * ptopref[2] + pmcptopglass[2];
261 ins->AddNode(mcptop, 1, new TGeoTranslation(0, 0, z));
262
263 TVirtualMC::GetMC()->Gsvolu("0MCP", "BOX", getMediumID(kAir), pmcp, 3); // glass
264 TGeoVolume* mcp = gGeoManager->GetVolume("0MCP");
265 z = -mInStart[2] + 2 * ptopref[2] + 2 * pmcptopglass[2] + 2 * preg[2] + pmcp[2];
266 ins->AddNode(mcp, 1, new TGeoTranslation(0, 0, z));
267
268 TVirtualMC::GetMC()->Gsvolu("0MSI", "BOX", getMediumID(kMCPwalls), pmcpside, 3); // glass
269 TGeoVolume* mcpside = gGeoManager->GetVolume("0MSI");
270 x = -pmcp[0] + pmcpside[0];
271 y = -pmcp[1] + pmcpside[1];
272 mcp->AddNode(mcpside, 1, new TGeoTranslation(x, y, 0));
273 x = pmcp[0] - pmcpside[0];
274 y = pmcp[1] - pmcpside[1];
275 mcp->AddNode(mcpside, 2, new TGeoTranslation(x, y, 0));
276 x = -pmcp[1] + pmcpside[1];
277 y = -pmcp[0] + pmcpside[0];
278 mcp->AddNode(mcpside, 3, new TGeoCombiTrans(x, y, 0, new TGeoRotation("R2", 90, 0, 0)));
279 x = pmcp[1] - pmcpside[1];
280 y = pmcp[0] - pmcpside[0];
281 mcp->AddNode(mcpside, 4, new TGeoCombiTrans(x, y, 0, new TGeoRotation("R2", 90, 0, 0)));
282
283 TVirtualMC::GetMC()->Gsvolu("0MBA", "BOX", getMediumID(kCeramic), pmcpbase, 3); // glass
284 TGeoVolume* mcpbase = gGeoManager->GetVolume("0MBA");
285 z = -mInStart[2] + 2 * ptopref[2] + pmcptopglass[2] + 2 * pmcp[2] + pmcpbase[2];
286 ins->AddNode(mcpbase, 1, new TGeoTranslation(0, 0, z));
287}
288
289//----------------------------------
290void Detector::SetCablesA(TGeoVolume* stl)
291{
292
293 float pcableplane[3] = {20, 20, 0.25}; //
294
295 TVirtualMC::GetMC()->Gsvolu("0CAA", "BOX", getMediumID(kAir), pcableplane, 3); // container for cables
296 TGeoVolume* cableplane = gGeoManager->GetVolume("0CAA");
297 // float zcableplane = -mStartA[2] + 2 * mInStart[2] + pcableplane[2];
298 int na = 0;
299 double xcell[24], ycell[24];
300 for (int imcp = 0; imcp < 24; imcp++) {
301 xcell[na] = mPosModuleAx[imcp];
302 ycell[na] = mPosModuleAy[imcp];
303 TGeoVolume* vol = SetCablesSize(imcp);
304 cableplane->AddNode(vol, na, new TGeoTranslation(xcell[na], ycell[na], 0));
305 na++;
306 }
307
308 // 12 cables extending beyond the frame
309 Float_t pcablesextend[3] = {2, 15, 0.245};
310 Float_t pcablesextendsmall[3] = {3, 2, 0.245};
311 Float_t* ppcablesextend[] = {pcablesextend, pcablesextend, pcablesextendsmall, pcablesextendsmall};
312 // left side
313 double xcell_side[] = {-mStartA[0] + pcablesextend[0], mStartA[0] - pcablesextend[0], 0, 0};
314 double ycell_side[] = {0, 0, -mStartA[1] + pcablesextendsmall[1], mStartA[1] - pcablesextendsmall[1]};
315
316 for (int icab = 0; icab < 4; icab++) {
317 const std::string volName = Form("CAB%2.i", 52 + icab);
318 TVirtualMC::GetMC()->Gsvolu(volName.c_str(), " BOX", getMediumID(kCable), ppcablesextend[icab], 3); // cables
319 TGeoVolume* vol = gGeoManager->GetVolume(volName.c_str());
320 cableplane->AddNode(vol, 1, new TGeoTranslation(xcell_side[icab], ycell_side[icab], 0));
321 }
322 float zcableplane = mStartA[2] - pcableplane[2] - 3;
323 stl->AddNode(cableplane, 1, new TGeoTranslation(0, 0, zcableplane));
324}
325//------------------------------------------
326
327TGeoVolume* Detector::SetCablesSize(int mod)
328{
329 int na = 0;
330 int ncells = Geometry::NCellsC;
331
332 int mcpcables[52] = {2, 1, 2, 1, 2,
333 2, 1, 1, 1, 2,
334 2, 1, 1, 2,
335 2, 1, 1, 1, 2,
336 2, 1, 2, 1, 2,
337 2, 2, 3, 3, 1,
338 1, 2, 2, 2, 2,
339 1, 1, 1, 1, 1,
340 1, 1, 1, 1, 1,
341 2, 2, 2, 2, 2,
342 2, 3, 3};
343
344 // cable D=0.257cm, Weight: 13 lbs/1000ft = 0.197g/cm; 1 piece 0.65cm
345 // 1st 8 pieces - tube 8*0.65cm = 5.2cm; V = 0.0531cm2 -> box {0.27*0.27*1}cm; W = 0.66g
346 // 2nd 24 pieces 24*0.65cm; V = 0.76 -> {0.44, 0.447 1}; W = 3.07g
347 // 3d 48 pieces 48*0.65cm; V = 1.53cm^3; ->box {0.66, 0.66, 1.}; W= 6.14g
348 double xcell[ncells], ycell[ncells], zcell[ncells];
349 float xsize[3] = {1.8, 1.8, 2.6}; //
350 float ysize[3] = {0.6, 1.7, 2.};
351 float zsize[3] = {0.1, 0.1, 0.1};
352 // for (int imcp = 0; imcp < Geometry::NCellsC; imcp++) {
353 int ic = mcpcables[mod];
354 float calblesize[3];
355 calblesize[0] = xsize[ic - 1];
356 calblesize[1] = ysize[ic - 1];
357 calblesize[2] = zsize[ic - 1];
358 const std::string volName = Form("CAB%2.i", mod);
359 TVirtualMC::GetMC()->Gsvolu(volName.c_str(), "BOX", getMediumID(kCable), calblesize, 3); // cables
360 TGeoVolume* vol = gGeoManager->GetVolume(volName.c_str());
361 LOG(debug) << "C cables " << mod << " " << volName << " " << ic;
362 return vol;
363}
364
366{
367 //
368 // Creates entries for alignable volumes associating the symbolic volume
369 // name with the corresponding volume path.
370 //
371 // First version (mainly ported from AliRoot)
372 //
373
374 LOG(info) << "Add FT0 alignable volumes";
375
376 if (!gGeoManager) {
377 LOG(fatal) << "TGeoManager doesn't exist !";
378 return;
379 }
380
381 TString volPath = Form("/cave_1/barrel_1");
382 // set A side
383 TString volPathA = volPath + Form("/FT0A_1");
384 TString symNameA = "FT0A";
385 LOG(info) << symNameA << " <-> " << volPathA;
386 if (!gGeoManager->SetAlignableEntry(symNameA.Data(), volPathA.Data())) {
387 LOG(fatal) << "Unable to set alignable entry ! " << symNameA << " : " << volPathA;
388 }
389 // set C side
390 TString volPathC = volPath + Form("/FT0C_1");
391 TString symNameC = "FT0C";
392 LOG(info) << symNameC << " <-> " << volPathC;
393 if (!gGeoManager->SetAlignableEntry(symNameC.Data(), volPathC.Data())) {
394 LOG(fatal) << "Unable to set alignable entry ! " << symNameA << " : " << volPathA;
395 }
396 TString volPathMod, symNameMod;
397 for (Int_t imod = 0; imod < Geometry::NCellsA + Geometry::NCellsC; imod++) {
398 TString volPath = (imod < Geometry::NCellsA) ? volPathA : volPathC;
399 volPathMod = volPath + Form("/0MOD_%d", imod);
400 symNameMod = Form("0MOD_%d", imod);
401 if (!gGeoManager->SetAlignableEntry(symNameMod.Data(), volPathMod.Data())) {
402 LOG(fatal) << (Form("Alignable entry %s not created. Volume path %s not valid", symNameMod.Data(), volPathMod.Data()));
403 }
404 }
405}
406
407//Construction of FT0-A support structure
408//The frame is constructed by first building a block of Aluminum as a mother volume from which details can
409//be subtracted. First, 6 boxe shapes are subtracted from around the edges and 2 from the center of the frame
410//to create the fin shapes at the edges of the detector and the cross shape at the center of the detector.
411//These boxe shapes are then subtracted again but from positions reflected in both x and y which is
412//reflects the symmetry of the frame. Then a loop is used to subtract out the PMT sockets which are also
413//box shapes from the positions given in the Geometry.cxx file. In the loop, after a socket is subtracted,
414//either an inner or an outer plate group is placed inside of it. Inner and outer plate groups are
415//both composed of a cover plate and a cable plate with fiber heads subtracted from the cable plate.
416//The orientation of these holes differs between the inner and outer plates making them distinct.
417//Contributors: Joe Crowley (2019-20), Jason Pruitt (2020-21), Sam Christensen (2021-22),
418//and Jennifer Klay (2019-22) from Cal Poly SLO.
419TGeoVolume* Detector::constructFrameAGeometry()
420{
421 // define the media
422 TGeoMedium* Vacuum = gGeoManager->GetMedium("FT0_Vacuum$");
423 TGeoMedium* Al = gGeoManager->GetMedium("FT0_Aluminium$");
424
425 // make a volume assembly for the frame
426 TGeoVolumeAssembly* FT0_Frame = new TGeoVolumeAssembly("FT0_Frame");
427
428 //Define the block of aluminum that forms the frame
429 Double_t blockdX = 37.1; //slightly larger in x
430 Double_t blockdY = 36.85; //than y
431 Double_t blockdZ = 6.95; //thickness of frame and back plates
432 TGeoBBox* block = new TGeoBBox("block", blockdX / 2, blockdY / 2, blockdZ / 2);
433
434 //To form the outer frame shape with fins that bolt it to the FV0, remove
435 //aluminum in six chunks (boxes) from two sides, then reflect these to remove
436 //from the other sides. As viewed from the back side of the detector, count
437 //clockwise from bottom left for numbering.
438 Double_t box1dX = 1.57; //narrower
439 Double_t box1dY = 6.55; //than it is tall
440 Double_t box1PosX = -(blockdX / 2 - box1dX / 2); //placement on the frame block
441 Double_t box1PosY = 0; //starts at the middle
442 TGeoBBox* box1 = new TGeoBBox("box1", box1dX / 2, box1dY / 2, blockdZ / 2);
443 TGeoTranslation* box1Tr1 = new TGeoTranslation("box1Tr1", box1PosX, box1PosY, 0);
444 box1Tr1->RegisterYourself();
445 TGeoTranslation* box1Tr2 = new TGeoTranslation("box1Tr2", -box1PosX, -box1PosY, 0);
446 box1Tr2->RegisterYourself();
447
448 Double_t box2dX = 2.9;
449 Double_t box2dY = 15.1;
450 Double_t box2PosX = -(blockdX / 2 - box2dX / 2);
451 Double_t box2PosY = blockdY / 2 - box2dY / 2;
452 TGeoBBox* box2 = new TGeoBBox("box2", box2dX / 2, box2dY / 2, blockdZ / 2);
453 TGeoTranslation* box2Tr1 = new TGeoTranslation("box2Tr1", box2PosX, box2PosY, 0);
454 box2Tr1->RegisterYourself();
455 TGeoTranslation* box2Tr2 = new TGeoTranslation("box2Tr2", -box2PosX, -box2PosY, 0);
456 box2Tr2->RegisterYourself();
457
458 //Box 3 is shallower than the others to preserve the aluminum fin where the
459 //FT0 is bolted to the FV0
460 Double_t box3dX = 12.7;
461 Double_t box3dY = 3;
462 Double_t box3dZ = 5.45;
463 Double_t box3PosX = -(blockdX / 2 - box2dX - box3dZ / 2);
464 Double_t box3PosY = blockdY / 2 - box3dY / 2;
465 Double_t box3PosZ = blockdZ / 2 - box3dZ / 2; //subtract from the back, leaving fin on the front
466 TGeoBBox* box3 = new TGeoBBox("box3", box3dX / 2, box3dY / 2, box3dZ / 2);
467 TGeoTranslation* box3Tr1 = new TGeoTranslation("box3Tr1", box3PosX, box3PosY, box3PosZ);
468 box3Tr1->RegisterYourself();
469 TGeoTranslation* box3Tr2 = new TGeoTranslation("box3Tr2", -box3PosX, -box3PosY, box3PosZ);
470 box3Tr2->RegisterYourself();
471
472 Double_t box4dX = 6.6;
473 Double_t box4dY = 1.67;
474 Double_t box4PosX = 0;
475 Double_t box4PosY = blockdY / 2 - box4dY / 2;
476 TGeoBBox* box4 = new TGeoBBox("box4", box4dX / 2, box4dY / 2, blockdZ / 2);
477 TGeoTranslation* box4Tr1 = new TGeoTranslation("box4Tr1", box4PosX, box4PosY, 0);
478 box4Tr1->RegisterYourself();
479 TGeoTranslation* box4Tr2 = new TGeoTranslation("box4Tr2", -box4PosX, -box4PosY, 0);
480 box4Tr2->RegisterYourself();
481
482 Double_t box5dX = 15;
483 Double_t box5dY = 3;
484 Double_t box5PosX = blockdX / 2 - box5dX / 2;
485 Double_t box5PosY = blockdY / 2 - box5dY / 2;
486 TGeoBBox* box5 = new TGeoBBox("box5", box5dX / 2, box5dY / 2, blockdZ / 2);
487 TGeoTranslation* box5Tr1 = new TGeoTranslation("box5Tr1", box5PosX, box5PosY, 0);
488 box5Tr1->RegisterYourself();
489 TGeoTranslation* box5Tr2 = new TGeoTranslation("box5Tr2", -box5PosX, -box5PosY, 0);
490 box5Tr2->RegisterYourself();
491
492 //Similar to box 3, box 6 is shallower in z to leave aluminum for the fin that
493 //bolts FT0 to FV0
494 Double_t box6dX = 2.9;
495 Double_t box6dY = 12.2;
496 Double_t box6dZ = 5.45;
497 Double_t box6PosX = blockdX / 2 - box6dX / 2;
498 Double_t box6PosY = blockdY / 2 - box5dY - box6dY / 2;
499 Double_t box6PosZ = blockdZ / 2 - box6dZ / 2; //subtract from the back, leaving fin at the front
500 TGeoBBox* box6 = new TGeoBBox("box6", box6dX / 2, box6dY / 2, box6dZ / 2);
501 TGeoTranslation* box6Tr1 = new TGeoTranslation("box6Tr1", box6PosX, box6PosY, box6PosZ);
502 box6Tr1->RegisterYourself();
503 TGeoTranslation* box6Tr2 = new TGeoTranslation("box6Tr2", -box6PosX, -box6PosY, box6PosZ);
504 box6Tr2->RegisterYourself();
505
506 //The central hole that accommodates the beam pipe is not the same on all four sides
507 //so we define two rectangular boxes - one vertical and one horizontal - and copy/rotate them
508 //to remove the aluminum in a "+" shape at the center
509 // cbox is a central rectangle
510 Double_t cbox1dX = 7.175; //horizontal center box
511 Double_t cbox1dY = 5.5;
512 Double_t cbox1Xoffset = 14.425;
513 Double_t cbox1PosX = -(blockdX / 2 - cbox1Xoffset - cbox1dX / 2);
514 Double_t cbox1PosY = 0;
515 TGeoBBox* cbox1 = new TGeoBBox("cbox1", cbox1dX / 2, cbox1dY / 2, blockdZ / 2);
516 TGeoTranslation* cbox1Tr1 = new TGeoTranslation("cbox1Tr1", cbox1PosX, cbox1PosY, 0);
517 cbox1Tr1->RegisterYourself();
518 TGeoTranslation* cbox1Tr2 = new TGeoTranslation("cbox1Tr2", -cbox1PosX, -cbox1PosY, 0);
519 cbox1Tr2->RegisterYourself();
520
521 Double_t cbox2dX = 5.75; //vertical center box
522
523 Double_t cbox2dY = 6.575;
524 Double_t cbox2Yoffset = 14.425;
525 Double_t cbox2PosX = 0;
526 Double_t cbox2PosY = blockdY / 2 - cbox2Yoffset - cbox2dY / 2;
527 TGeoBBox* cbox2 = new TGeoBBox("cbox2", cbox2dX / 2, cbox2dY / 2, blockdZ / 2);
528 TGeoTranslation* cbox2Tr1 = new TGeoTranslation("cbox2Tr1", cbox2PosX, cbox2PosY, 0);
529 cbox2Tr1->RegisterYourself();
530 TGeoTranslation* cbox2Tr2 = new TGeoTranslation("cbox2Tr2", -cbox2PosX, -cbox2PosY, 0);
531 cbox2Tr2->RegisterYourself();
532
533 //The two L-shaped pieces that form the frame have a small 1mm gap between them,
534 //where they come together. As viewed from the back, the gaps are on the upper
535 //left and lower right, so that for the center column of modules, the upper two
536 //are shifted slightly to the right (as viewed from the back) and the lower two
537 //are shifted slightly to the left (as viewed from the back)
538 Double_t gapBoxdX = 0.1;
539 Double_t gapBoxdY = blockdY / 2;
540 Double_t gapPosX = -(sPmtSide / 2 + sEps + gapBoxdX / 2);
541 Double_t gapPosY = blockdY / 4;
542 TGeoBBox* gapBox = new TGeoBBox("gapBox", gapBoxdX / 2, gapBoxdY / 2, blockdZ / 2);
543 TGeoTranslation* gapBoxTr1 = new TGeoTranslation("gapBoxTr1", gapPosX, gapPosY, 0);
544 gapBoxTr1->RegisterYourself();
545 TGeoTranslation* gapBoxTr2 = new TGeoTranslation("gapBoxTr2", -gapPosX, -gapPosY, 0);
546 gapBoxTr2->RegisterYourself();
547
548 //Create a string to define the complete frame object shape
549 //Start from the aluminum block then subtract the boxes
550 std::string frameACompositeString = "block ";
551 frameACompositeString += "- box1:box1Tr1 - box1:box1Tr2 ";
552 frameACompositeString += "- box2:box2Tr1 - box2:box2Tr2 ";
553 frameACompositeString += "- box3:box3Tr1 - box3:box3Tr2 ";
554 frameACompositeString += "- box4:box4Tr1 - box4:box4Tr2 ";
555 frameACompositeString += "- box5:box5Tr1 - box5:box5Tr2 ";
556 frameACompositeString += "- box6:box6Tr1 - box6:box6Tr2 ";
557 frameACompositeString += "- cbox1:cbox1Tr1 - cbox1:cbox1Tr2 ";
558 frameACompositeString += "- cbox2:cbox2Tr1 - cbox2:cbox2Tr2 ";
559 frameACompositeString += "- gapBox:gapBoxTr1 - gapBox:gapBoxTr2";
560
561 //The next section defines the objects that form the sockets in the
562 //frame for the sensitive elements and the individual cover plates
563 //at the front of the detector which include the optical fiber
564 //heads that permit the LED pulser light to reach the quartz radiator
565 //surfaces of each module
566
567 //There are two fiber head configurations, called "inner" and "outer"
568 //with different locations and angles of the fiber heads.
569 Double_t coverPlatedZ = 0.2; //top cover thickness
570 Double_t fiberPlatedZ = 0.5; //fiberhead plate is underneath
571
572 //Each fiber is guided to a small rectangular opening in the plate
573 Double_t opticalFiberHeaddY = 0.52; //narrow side
574 Double_t opticalFiberHeaddX = 1.142; //long side
575
576 //The "top" two fiber heads are positioned at slightly different
577 //locations than the bottom two, which are also rotated
578
579 //"Outer" fiberhead placements
580 Double_t fh1TopPosX = -1.6;
581 Double_t fh1TopPosY = 1.325;
582 Double_t fh1BotPosX = 1.555;
583 Double_t fh1BotPosY = 1.249;
584 Double_t fh1BotAngle = 16; //degrees
585
586 //"Inner" fiberhead placements
587 //All of these are placed at an angle
588 Double_t fh2TopPosX = -1.563;
589 Double_t fh2TopPosY = 1.4625;
590 Double_t fh2TopAngle = 60;
591
592 Double_t fh2BotPosX = 1.084;
593 Double_t fh2BotPosY = 1.186;
594 Double_t fh2BotAngle = -30;
595
596 // Define cover plate, fiber plate, and optical Fiber Head shapes
597 TGeoBBox* coverPlate = new TGeoBBox("coverPlate", sPmtSide / 2 + sEps, sPmtSide / 2 + sEps, coverPlatedZ / 2);
598 TGeoBBox* fiberPlate = new TGeoBBox("fiberPlate", sPmtSide / 2 + sEps, sPmtSide / 2 + sEps, fiberPlatedZ / 2);
599 TGeoBBox* opticalFiberHead = new TGeoBBox("opticalFiberHead", opticalFiberHeaddX / 2, opticalFiberHeaddY / 2, fiberPlatedZ / 2);
600
601 // Define transformations of optical fiber heads for outer plate
602 TGeoTranslation* coverPlateTr = new TGeoTranslation("coverPlateTr", 0, 0, fiberPlatedZ / 2 + coverPlatedZ / 2);
603 coverPlateTr->RegisterYourself();
604 TGeoTranslation* fh1TopTr1 = new TGeoTranslation("fh1TopTr1", fh1TopPosX, fh1TopPosY, 0);
605 fh1TopTr1->RegisterYourself();
606 TGeoTranslation* fh1TopTr2 = new TGeoTranslation("fh1TopTr2", fh1TopPosX, -fh1TopPosY, 0);
607 fh1TopTr2->RegisterYourself();
608 TGeoCombiTrans* fh1BotTr1 = new TGeoCombiTrans("fh1BotTr1", fh1BotPosX, fh1BotPosY, 0, new TGeoRotation("fh1BotRot1", fh1BotAngle, 0, 0));
609 fh1BotTr1->RegisterYourself();
610 TGeoCombiTrans* fh1BotTr2 = new TGeoCombiTrans("fh1BotTr2", fh1BotPosX, -fh1BotPosY, 0, new TGeoRotation("fh1BotRot2", -fh1BotAngle, 0, 0));
611 fh1BotTr2->RegisterYourself();
612 TGeoCombiTrans* fh2TopTr1 = new TGeoCombiTrans("fh2TopTr1", fh2TopPosX, fh2TopPosY, 0, new TGeoRotation("fh2TopRot1", fh2TopAngle + 90, 0, 0));
613 fh2TopTr1->RegisterYourself();
614 TGeoCombiTrans* fh2TopTr2 = new TGeoCombiTrans("fh2TopTr2", fh2TopPosX, -fh2TopPosY, 0, new TGeoRotation("fh2TopRot2", -fh2TopAngle - 90, 0, 0));
615 fh2TopTr2->RegisterYourself();
616 TGeoCombiTrans* fh2BotTr1 = new TGeoCombiTrans("fh2BotTr1", fh2BotPosX, fh2BotPosY, 0, new TGeoRotation("fh2BotRot1", -fh2BotAngle, 0, 0));
617 fh2BotTr1->RegisterYourself();
618 TGeoCombiTrans* fh2BotTr2 = new TGeoCombiTrans("fh2BotTr2", fh2BotPosX, -fh2BotPosY, 0, new TGeoRotation("fh2BotRot2", fh2BotAngle, 0, 0));
619 fh2BotTr2->RegisterYourself();
620
621 //Create a string that defines the plate group for the outer plates
622 std::string outerPlateGroupString = "fiberPlate ";
623 outerPlateGroupString += "- opticalFiberHead:fh1TopTr1 ";
624 outerPlateGroupString += "- opticalFiberHead:fh1TopTr2 ";
625 outerPlateGroupString += "- opticalFiberHead:fh1BotTr1 ";
626 outerPlateGroupString += "- opticalFiberHead:fh1BotTr2 ";
627 outerPlateGroupString += "+ coverPlate:coverPlateTr";
628
629 //Create the composite shape for the outer plates
630 TGeoCompositeShape* outerPlateGroup = new TGeoCompositeShape("outerPlateGroup", outerPlateGroupString.c_str());
631
632 //Create a string that defines the plate group for the inner plates
633 std::string innerPlateGroupString = "fiberPlate ";
634 innerPlateGroupString += "- opticalFiberHead:fh2TopTr1 ";
635 innerPlateGroupString += "- opticalFiberHead:fh2TopTr2 ";
636 innerPlateGroupString += "- opticalFiberHead:fh2BotTr1 ";
637 innerPlateGroupString += "- opticalFiberHead:fh2BotTr2 ";
638 innerPlateGroupString += "+ coverPlate:coverPlateTr";
639
640 //Create the composite shape for the inner plates
641 TGeoCompositeShape* innerPlateGroup = new TGeoCompositeShape("innerPlateGroup", innerPlateGroupString.c_str());
642
643 //The sockets that are cut out of the aluminum block for the senitive elements
644 //to fit into are offset slightly in z to leave a thin plate of aluminum at the
645 //back - the back plate covers
646 Double_t backPlanedZ = 0.25;
647 Double_t socketdZ = blockdZ - backPlanedZ;
648
649 //Define the socket volume as a box of vacuum
650 TGeoVolume* socket = gGeoManager->MakeBox("Socket", Vacuum, sPmtSide / 2 + sEps, sPmtSide / 2 + sEps, socketdZ / 2);
651
652 //Define the orientation angles of the plate groups that will cover
653 //the sockets holding the sensitive elements
654 Double_t rotAngle[Geometry::NCellsA] = {0, 0, -90, -90, -90, 0, 0, -90, -90, -90, 0, 0, 180, 180, 90, 90, 90, 180, 180, 90, 90, 90, 180, 180};
655 //Define the socket and plate group translations
656 TGeoTranslation* trSocket[Geometry::NCellsA];
657 TString nameTrSocket;
658 TGeoCombiTrans* trPlateGroup[Geometry::NCellsA];
659 TString nameTrPlateGroup;
660 TString namePGRot;
661
662 //Loop over the number of modules, subtracting the sockets and adding back in the
663 //plate groups at the position of each module
664 for (Int_t itr = 0; itr < Geometry::NCellsA; itr++) {
665
666 nameTrSocket = Form("trSocket%i", itr + 1);
667 float z = -backPlanedZ / 4.0;
668 trSocket[itr] = new TGeoTranslation(nameTrSocket.Data(), mPosModuleAx[itr], mPosModuleAy[itr], z);
669 trSocket[itr]->RegisterYourself();
670 frameACompositeString += "- Socket:"; //subtract it from the aluminum block
671 frameACompositeString += nameTrSocket.Data(); //at its corresponding location
672
673 nameTrPlateGroup = Form("trPlateGroup%i", itr + 1);
674 namePGRot = Form("pgRot%i", itr + 1);
675 float z2 = -blockdZ / 2 + (coverPlatedZ + fiberPlatedZ) / 2;
676 trPlateGroup[itr] = new TGeoCombiTrans(nameTrPlateGroup.Data(), mPosModuleAx[itr], mPosModuleAy[itr], z2, new TGeoRotation(namePGRot.Data(), rotAngle[itr], 0, 0));
677 trPlateGroup[itr]->RegisterYourself();
678
679 if (itr == 0 || itr == 2 || itr == 3 || itr == 4 || itr == 5 || itr == 10 || itr == 13 || itr == 18 || itr == 19 || itr == 20 || itr == 21 || itr == 23) {
680 frameACompositeString += " + outerPlateGroup:"; //add the outer plate group back on to these modules
681 frameACompositeString += nameTrPlateGroup.Data();
682 frameACompositeString += " ";
683 } else {
684 frameACompositeString += " + innerPlateGroup:"; //or add the inner plate group back on to all other modules
685 frameACompositeString += nameTrPlateGroup.Data();
686 frameACompositeString += " ";
687 }
688 }
689
690 //Finally, define the A side frame object from the complete composite shape defined above
691 TGeoVolume* frameA = new TGeoVolume("frameA", new TGeoCompositeShape("frameA", frameACompositeString.c_str()), Al);
692
693 //Add the frame object to the mother volume
694 FT0_Frame->AddNode(frameA, 1);
695
696 return FT0_Frame;
697}
698
699//C-side Support Structure
700//This code was written by Jason Pruitt and Sam Christensen of Cal Poly in 2021
701//They followed a similar method as for the A-side frame but had to account for the
702//spherical geometry of the C-side.
703TGeoVolume* Detector::constructFrameCGeometry()
704{
705
706 // define the media
707 TGeoMedium* Vacuum = gGeoManager->GetMedium("FT0_Vacuum$");
708 TGeoMedium* Al = gGeoManager->GetMedium("FT0_Aluminium$");
709 static constexpr Double_t sFrameZC = 5.5632;
710 static constexpr Double_t frameHeightC = 2.5; // pinstart[2] or l_s
711
712 // quartz & PMT C-side transformations
713 static constexpr Double_t sensShift = 0.5;
714 static constexpr Double_t sQuartzRadiatorZC = 1.94360; // Dimension variable (l_{q}
715 static constexpr Double_t sQuartzHeightC = (-sFrameZC / 2 + sQuartzRadiatorZC / 2); // placement variable )
716 static constexpr Double_t sPmtZC = 3.600; // Dimension variable (l_{p}
717 static constexpr Double_t sPmtHeightC = (sFrameZC / 2 - sPmtZC / 2); // placement variable
718
719 Double_t crad = 82.;
720 static constexpr Int_t NCellsC = Geometry::NCellsC;
721 static constexpr Int_t NCellsA = Geometry::NCellsA;
722
723 Float_t sweep = 3.5 * 2;
724 Float_t rMin = 81.9791;
725 Float_t rMax = rMin + sFrameZC;
726 Float_t tMin = 0;
727 Float_t tMax = 35;
728 Float_t pMin = 0;
729 Float_t pMax = 180;
730 Float_t pinstart[3] = {2.9491, 2.9491, 2.5};
731 Float_t pstartC[3] = {20., 20, 5};
732
733 Float_t multCorn = 1.275; // multiplication factor for corners
734 Double_t xCorn = multCorn * (-14.75272569);
735 Double_t yCorn = multCorn * (14.9043284);
736 Double_t zCorn = 79.27306024;
737
738 Double_t xCorn2 = -xCorn;
739 Double_t yCorn2 = yCorn;
740 Double_t zCorn2 = zCorn;
741
742 Double_t acCorn = TMath::ATan(yCorn / xCorn) - TMath::Pi() / 2 + 2 * TMath::Pi();
743 Double_t bcCorn = /*(-1)**/ TMath::ACos(zCorn / crad);
744 Double_t gcCorn = -1 * acCorn;
745
746 // holepunch corners not implemented for quartzRadiatorSeat, rounded corners are
747 // in place for PMT
748 Double_t flopsErr = 0.00001;
749 Double_t exag = 5;
750
751 // highest overlap values
752 Double_t errPMTZ = 10 * sEps;
753 Double_t errPMTXY = 0.02;
754 Double_t errQrdZ = 0.143 + 0.22;
755 Double_t errQrdXY = 0.35;
756
757 Float_t backPlateZ = 0.5;
758
759 // sphere1 is the spherical shell that will be subtracted
760 // to approximate the c-side support frame and the subsequent
761 // spheres clip the shape with curvature preserved
762 TGeoSphere* sphere1 = new TGeoSphere("sphere1", rMin, rMax, tMin, tMax, pMin, pMax);
763 TGeoSphere* sphere2 = new TGeoSphere("sphere2", rMin - sweep, rMax + sweep, tMin, tMax, pMin, pMax);
764 TGeoSphere* sphere3 = new TGeoSphere("sphere3", rMin, rMin + backPlateZ, tMin, tMax, pMin, pMax);
765 TGeoSphere* sphere4 = new TGeoSphere("sphere4", rMin - sweep, rMax + backPlateZ + sweep, tMin, tMax, pMin, pMax);
766
767 TGeoBBox* insSeat = new TGeoBBox("insSeat", pinstart[0] * 2, pinstart[1] * 2, pinstart[2] * 2);
768
769 TGeoBBox* quartzRadiatorSeat = new TGeoBBox("quartzRadiatorSeat",
770 sQuartzRadiatorSide / 2 + sEps + errQrdXY,
771 sQuartzRadiatorSide / 2 + sEps + errQrdXY,
772 sQuartzRadiatorZC / 2 + sEps + errQrdZ);
773
774 TGeoBBox* pmtBoxSeat = new TGeoBBox("pmtBoxSeat",
775 sPmtSide / 2 + sEps + errPMTXY,
776 sPmtSide / 2 + sEps + errPMTXY,
777 sPmtZ / 2 + sEps + errPMTZ);
778 TGeoBBox* pmtCornerRect = new TGeoBBox("pmtCornerRect",
779 sCornerRadius / 2 - flopsErr,
780 sCornerRadius / 2 - flopsErr,
781 sPmtZ / 2);
782
783 TGeoBBox* framecornerBox = new TGeoBBox("framecornerBox", 5, 5, 10);
784
785 // C-side transformations
786 TGeoRotation* rot1 = new TGeoRotation("rot1", 90, 0, 0);
787 rot1->RegisterYourself();
788 TGeoCombiTrans* rotTr1 = new TGeoCombiTrans("rotTr1", -20, -1, -5, rot1); // cuts off left side of shell
789 rotTr1->RegisterYourself();
790
791 TGeoRotation* rot2 = new TGeoRotation("rot2", -90, 0, 0);
792 rot2->RegisterYourself();
793 TGeoCombiTrans* rotTr2 = new TGeoCombiTrans("rotTr2", 20, -1, -5, rot2);
794 rotTr2->RegisterYourself();
795
796 TGeoRotation* rot3 = new TGeoRotation("rot3", 0, 0, 0);
797 rot3->RegisterYourself();
798 TGeoCombiTrans* rotTr3 = new TGeoCombiTrans("rotTr3", 0, 20, -5, rot3);
799 rotTr3->RegisterYourself();
800
801 TGeoTranslation* centerTrans = new TGeoTranslation("centerTrans", 0, 0, 85);
802 centerTrans->RegisterYourself();
803
804 TGeoRotation* reflectC1 = new TGeoRotation("reflectC1", 0, 0, 0);
805 reflectC1->ReflectX(true);
806 reflectC1->ReflectY(true);
807 reflectC1->RegisterYourself();
808
809 TGeoRotation* rotCorners = new TGeoRotation("rotCorners", acCorn, bcCorn, gcCorn);
810 rotCorners->RegisterYourself();
811
812 TGeoCombiTrans* comCorners = new TGeoCombiTrans("comCorners", xCorn, yCorn, zCorn, rotCorners);
813 comCorners->RegisterYourself();
814
815 TGeoCombiTrans* comCorners2 = new TGeoCombiTrans("comCorners2", xCorn2, yCorn2, zCorn2, rotCorners);
816 comCorners2->RegisterYourself();
817
818 //Create a string that defines the composite shape
819 std::string shellString = "";
820 shellString += "sphere1"; // start with spherical shell - this will be reflected
821 shellString += "- sphere2:rotTr1"; // copy and combitrans a subtraction
822 shellString += "- sphere2:rotTr2"; //
823 shellString += "- sphere2:rotTr3"; //
824 shellString += "- insSeat:centerTrans"; // subtract center
825 shellString += "- framecornerBox:comCorners"; // subtract corners
826 shellString += "- framecornerBox:comCorners2"; //
827
828 //Create string that defines the back plate composite shape
829 std::string backPlateString = "";
830 backPlateString += "sphere3";
831 backPlateString += "- sphere4:rotTr1";
832 backPlateString += "- sphere4:rotTr2";
833 backPlateString += "- sphere4:rotTr3";
834 backPlateString += "- insSeat:centerTrans";
835 backPlateString += "- framecornerBox:comCorners";
836 backPlateString += "- framecornerBox:comCorners2";
837
838 //These could be set up to use the values in the geometry file after some
839 //investigation of subtle differences...
840 static constexpr Double_t xi[NCellsC] = {-15.038271418735729, 15.038271418735729, -15.003757581112167, 15.003757581112167, -9.02690018974363, 9.02690018974363, -9.026897413747076, 9.026897413747076, -9.026896531935773, 9.026896531935773, -3.0004568618531313, 3.0004568618531313, -3.0270795197907225, 3.0270795197907225, 3.0003978432927543, -3.0003978432927543, 3.0270569670429572, -3.0270569670429572, 9.026750365564254, -9.026750365564254, 9.026837450695885, -9.026837450695885, 9.026849243816981, -9.026849243816981, 15.038129472387304, -15.038129472387304, 15.003621961057961, -15.003621961057961};
841 static constexpr Double_t yi[NCellsC] = {3.1599494336464455, -3.1599494336464455, 9.165191680982874, -9.165191680982874, 3.1383331772537426, -3.1383331772537426, 9.165226363918643, -9.165226363918643, 15.141616002932361, -15.141616002932361, 9.16517861649866, -9.16517861649866, 15.188854859073416, -15.188854859073416, 9.165053319552113, -9.165053319552113, 15.188703787345304, -15.188703787345304, 3.138263189805292, -3.138263189805292, 9.165104089644917, -9.165104089644917, 15.141494417823818, -15.141494417823818, 3.1599158563428644, -3.1599158563428644, 9.165116302773846, -9.165116302773846};
842
843 Double_t zi[NCellsC];
844 for (Int_t ic = 0; ic < NCellsC; ic++) {
845 zi[ic] = TMath::Sqrt(TMath::Power(crad, 2) - TMath::Power(xi[ic], 2) - TMath::Power(yi[ic], 2));
846 }
847
848 // get rotation data
849 Double_t ac[NCellsC], bc[NCellsC], gc[NCellsC];
850 for (Int_t i = 0; i < NCellsC; i++) {
851 ac[i] = TMath::ATan(yi[i] / xi[i]) - TMath::Pi() / 2 + 2 * TMath::Pi();
852 if (xi[i] < 0) {
853 bc[i] = TMath::ACos(zi[i] / crad);
854 } else {
855 bc[i] = -1 * TMath::ACos(zi[i] / crad);
856 }
857 }
858
859 Double_t xc2[NCellsC], yc2[NCellsC], zc2[NCellsC];
860
861 // compensation based on node position within individual detector geometries
862 // determine compensated radius
863 Double_t rcomp = crad + pstartC[2] / 2.0;
864 for (Int_t i = 0; i < NCellsC; i++) {
865 // Get compensated translation data
866 xc2[i] = rcomp * TMath::Cos(ac[i] + TMath::Pi() / 2) * TMath::Sin(-1 * bc[i]);
867 yc2[i] = rcomp * TMath::Sin(ac[i] + TMath::Pi() / 2) * TMath::Sin(-1 * bc[i]);
868 zc2[i] = rcomp * TMath::Cos(bc[i]);
869
870 // Convert angles to degrees
871 ac[i] *= 180 / TMath::Pi();
872 bc[i] *= 180 / TMath::Pi();
873 gc[i] = -1 * ac[i];
874 }
875
876 Double_t rmag = sqrt(xc2[0] * xc2[0] + yc2[0] * yc2[0] + zc2[0] * zc2[0]);
877
878 Double_t scalePMT = (rmag + (frameHeightC / 2.0) - (sPmtHeightC / 2)) / rmag;
879 Double_t scaleQrad = (rmag + (frameHeightC / 2.0) - sPmtHeightC - (sQuartzRadiatorZC / 2.0)) / rmag;
880
881 Double_t xPMT[NCellsC];
882 Double_t yPMT[NCellsC];
883 Double_t zPMT[NCellsC];
884
885 Double_t xQrad[NCellsC];
886 Double_t yQrad[NCellsC];
887 Double_t zQrad[NCellsC];
888
889 Double_t rotC[NCellsC];
890 Double_t comC[NCellsC];
891
892 for (Int_t i = 0; i < NCellsC; i++) {
893 // PMT Transformations
894 xPMT[i] = scalePMT * xc2[i];
895 yPMT[i] = scalePMT * yc2[i];
896 zPMT[i] = scalePMT * zc2[i];
897
898 // Quartz radiator transformations
899 xQrad[i] = scaleQrad * xc2[i];
900 yQrad[i] = scaleQrad * yc2[i];
901 zQrad[i] = scaleQrad * zc2[i];
902 }
903
904 TString nameRot;
905 TString nameComPMT;
906 TString nameComQuartz;
907 TString nameComPlates;
908 TString nameComC;
909
910 for (Int_t itr = NCellsA; itr < NCellsA + NCellsC; itr++) {
911 nameRot = Form("0Rot%i", itr + 1);
912 int ic = itr - NCellsA;
913 nameComPMT = Form("0ComPMT%i", ic + 1);
914 nameComQuartz = Form("0ComQuartz%i", ic + 1);
915
916 // getting even indices to skip reflections -> reflections happen later in
917 // frame construction
918 if (ic % 2 == 0) {
919 TGeoRotation* rotC = new TGeoRotation(nameRot.Data(), ac[ic], bc[ic], gc[ic]);
920 rotC->RegisterYourself();
921
922 TGeoCombiTrans* comC = new TGeoCombiTrans(nameComC.Data(), xc2[ic], yc2[ic], zc2[ic], rotC);
923 comC->RegisterYourself();
924
925 TGeoRotation* rotPMT = new TGeoRotation(nameRot.Data(), ac[ic], bc[ic], gc[ic]);
926 rotPMT->RegisterYourself();
927
928 TGeoCombiTrans* comPMT = new TGeoCombiTrans(nameComPMT.Data(),
929 xPMT[ic], yPMT[ic],
930 zPMT[ic], rotPMT);
931 comPMT->RegisterYourself();
932
933 TGeoRotation* rotQuartz = new TGeoRotation(nameRot.Data(),
934 ac[ic], bc[ic], gc[ic]);
935 rotQuartz->RegisterYourself();
936
937 TGeoCombiTrans* comQuartz = new TGeoCombiTrans(nameComQuartz.Data(),
938 xQrad[ic], yQrad[ic],
939 zQrad[ic] - (sQuartzRadiatorZC / 2 + 3 * sEps),
940 rotQuartz);
941 comQuartz->RegisterYourself();
942
943 TGeoRotation* rotPlates = new TGeoRotation(nameRot.Data(),
944 ac[ic], bc[ic], gc[ic]);
945 rotPlates->RegisterYourself();
946 TGeoCombiTrans* comPlates = new TGeoCombiTrans(nameComPlates.Data(),
947 xQrad[ic], yQrad[ic],
948 zQrad[ic],
949 rotPlates);
950 comPlates->RegisterYourself();
951
952 // Subtract the PMTs from the frame
953 std::string pmtCombiString = "";
954 pmtCombiString += "- ";
955 pmtCombiString += "pmtBoxSeat:";
956 pmtCombiString += nameComPMT.Data();
957 shellString += pmtCombiString;
958
959 // Subtract the QuartzRadiators from the frame
960 std::string quartzCombiString = "";
961 quartzCombiString += "- ";
962 quartzCombiString += "quartzRadiatorSeat:";
963 quartzCombiString += nameComQuartz.Data();
964 shellString += quartzCombiString;
965 }
966 }
967
968 // Construct composite shape from boolean
969 TGeoCompositeShape* shellCompShape = new TGeoCompositeShape("shellCompShape", shellString.c_str());
970
971 TGeoVolume* shellVol = new TGeoVolume("shellVol", shellCompShape, Al);
972
973 // frame mother assembly
974 TGeoVolumeAssembly* FT0_C_Frame = new TGeoVolumeAssembly("FT0_C_Frame");
975
976 // placement and reflections of frame approxes
977 TGeoTranslation* shellTr1 = new TGeoTranslation("shellTr1", 0, 0, -80);
978 shellTr1->RegisterYourself();
979
980 TGeoCombiTrans* shellTr2 = new TGeoCombiTrans("shellTr2", 0, 0, -80, reflectC1);
981 shellTr2->RegisterYourself();
982
983 FT0_C_Frame->AddNode(shellVol, 1, shellTr1);
984 FT0_C_Frame->AddNode(shellVol, 2, shellTr2);
985
986 TGeoTranslation* backPlateTr1 = new TGeoTranslation("backPlateTr1", 0, 0, -74);
987 backPlateTr1->RegisterYourself();
988
989 TGeoCombiTrans* backPlateTr2 = new TGeoCombiTrans("backPlateTr2", 0, 0, -74, reflectC1);
990 backPlateTr2->RegisterYourself();
991
992 TGeoCompositeShape* backPlateShape = new TGeoCompositeShape("backPlateShape", backPlateString.c_str());
993 TGeoVolume* backPlateVol = new TGeoVolume("backPlateVol", backPlateShape, Al);
994
995 FT0_C_Frame->AddNode(backPlateVol, 3, backPlateTr1);
996 FT0_C_Frame->AddNode(backPlateVol, 4, backPlateTr2);
997
998 return FT0_C_Frame;
999}
1000
1001std::string Detector::cPlateShapeString()
1002{
1003 Double_t prismHeight = 0.3895; //height of vertical edge of square prism part of base
1004 Double_t prismSide = 5.9; //width and length of square prism part of base
1005 Double_t radCurve = 81.9469; //radius of curvature of top part of base
1006 Double_t delHeight = radCurve * (1.0 - TMath::Sqrt(1.0 - 0.5 * TMath::Power(prismSide / radCurve, 2.0)));
1007 //height from top of square prism to center of curved top surface of base
1008
1009 Double_t heightBase = prismHeight + delHeight; //from center of bottom to center of top
1010 Double_t sliceSide = 5.3; //side lengths of slice's flat top
1011 Double_t heightBaseBox = 2 * heightBase;
1012 Double_t totalHeight = 0.5;
1013 Double_t sliceHeight = 0.5 - heightBase;
1014
1015 //cable dimensions and distances
1016 Double_t cableHoleWidth = 0.3503;
1017 Double_t cableHoleLength = 0.9003;
1018 Double_t cableHoleDepth = 1; //really big just to punch a hole
1019
1020 //sholes denotes "straight holes" and rholes denote "rotated holes"
1021 //all distances measured from edges of slice
1022 //up and down sholes
1023 Double_t sHolesBottomEdge = 1.585;
1024 Double_t sHolesTopEdge = 0.515;
1025 Double_t sHolesAvgTopBottom = (sHolesBottomEdge + sHolesTopEdge) / 2.0;
1026 Double_t sHolesUpFromCenter = ((sliceSide / 2.0) - sHolesAvgTopBottom); //amount up in x the sholes need to move
1027 //left and right sholes
1028 Double_t sHolesFarEdge = 1.585;
1029 Double_t sHolesNearEdge = 1.065;
1030 Double_t sHolesAvgNearFar = (sHolesFarEdge + sHolesNearEdge) / 2.0;
1031 Double_t sHolesLateralFromCenter = ((sliceSide / 2.0) - sHolesAvgNearFar);
1032
1033 // Create Boxes
1034 TGeoBBox* box = new TGeoBBox("BASE", prismSide / 2.0, heightBaseBox / 2.0, prismSide / 2.0);
1035
1036 // Base raw box to be subtracted
1037 TGeoBBox* slice = new TGeoBBox("SLICE", sliceSide / 2.0, heightBaseBox / 2.0, sliceSide / 2.0);
1038 TGeoBBox* cableHole = new TGeoBBox("CABLE", cableHoleLength / 2.0, cableHoleDepth / 2.0, cableHoleWidth / 2.0);
1039 TGeoBBox* cableHole2 = new TGeoBBox("CABLE2", cableHoleWidth / 2.0, cableHoleLength / 2.0, cableHoleDepth / 2.0);
1040
1041 TGeoSphere* baseShape = new TGeoSphere("BASE_SUBTRACTION", radCurve, radCurve + 5.0, 80, 100, 80, 100);
1042
1043 TGeoTranslation* rTrans = new TGeoTranslation("rTrans", 0, radCurve, 0);
1044 rTrans->RegisterYourself();
1045
1046 TGeoTranslation* rBackTrans = new TGeoTranslation("rBackTrans", 0, -1.0 * radCurve, 0);
1047 rBackTrans->RegisterYourself();
1048
1049 TGeoTranslation* subSliceTrans = new TGeoTranslation("subSliceTrans", 0, (heightBaseBox / 2.0) + sliceHeight, 0);
1050 subSliceTrans->RegisterYourself();
1051
1052 TGeoTranslation* sHolesTopLeftTrans = new TGeoTranslation("sHolesTopLeftTrans", sHolesUpFromCenter, 0, sHolesLateralFromCenter);
1053 sHolesTopLeftTrans->RegisterYourself();
1054
1055 TGeoTranslation* sHolesTopRightTrans = new TGeoTranslation("sHolesTopRightTrans", sHolesUpFromCenter, 0, -1.0 * sHolesLateralFromCenter);
1056 sHolesTopRightTrans->RegisterYourself();
1057
1058 TGeoTranslation* testTrans = new TGeoTranslation("testTrans", 0.1, 0.1, 0);
1059 testTrans->RegisterYourself();
1060
1061 TGeoRotation* switchToZ = new TGeoRotation("switchToZ", 90, 90, 0);
1062 switchToZ->RegisterYourself();
1063
1064 TGeoRotation* rotateHolesLeft = new TGeoRotation("rotateHolesLeft", 345, 0, 0);
1065 rotateHolesLeft->RegisterYourself();
1066
1067 TGeoRotation* rotateHolesRight = new TGeoRotation("rotatetHolesRight", 15, 0, 0);
1068 rotateHolesRight->RegisterYourself();
1069
1070 // Bottom holes rotation and translation with combitrans
1071 TGeoCombiTrans* rHolesBottomLeftTrans = new TGeoCombiTrans("rHolesBottomLeftTrans", -1.0 * sHolesLateralFromCenter, -1.0 * sHolesUpFromCenter, 0, rotateHolesLeft);
1072 rHolesBottomLeftTrans->RegisterYourself();
1073
1074 TGeoCombiTrans* rHolesBottomRightTrans = new TGeoCombiTrans("rHolesBottomRightTrans", sHolesLateralFromCenter, -1.0 * sHolesUpFromCenter, 0, rotateHolesRight);
1075 rHolesBottomRightTrans->RegisterYourself();
1076
1077 std::string plateString = " ";
1078 plateString += "(((BASE:rTrans";
1079 plateString += "- BASE_SUBTRACTION)";
1080 plateString += "+ (SLICE:rTrans))";
1081 plateString += ":rBackTrans";
1082 plateString += "- BASE:subSliceTrans";
1083 plateString += "- (CABLE:sHolesTopLeftTrans)";
1084 plateString += "- (CABLE:sHolesTopRightTrans))";
1085 plateString += ":switchToZ";
1086 plateString += "- (CABLE2:rHolesBottomLeftTrans)";
1087 plateString += "- (CABLE2:rHolesBottomRightTrans)";
1088
1089 return plateString;
1090}
1091//End Support structure code
1093
1094Bool_t Detector::ProcessHits(FairVolume* v)
1095{
1096
1097 Int_t copy;
1098 Int_t volID = fMC->CurrentVolID(copy);
1099
1100 TVirtualMCStack* stack = fMC->GetStack();
1101 Int_t quadrant, mcp;
1102 if (fMC->IsTrackEntering()) {
1103 float x, y, z;
1104 fMC->TrackPosition(x, y, z);
1105 fMC->CurrentVolID(quadrant);
1106 fMC->CurrentVolOffID(1, mcp);
1107 float time = fMC->TrackTime() * 1.0e9; // time from seconds to ns
1108 int trackID = stack->GetCurrentTrackNumber();
1109 int detID = mSim2LUT[4 * mcp + quadrant - 1];
1110 int iPart = fMC->TrackPid();
1111 if (fMC->TrackCharge() && volID == mREGVolID) { // charge particles for MCtrue
1112 AddHit(x, y, z, time, 10, trackID, detID);
1113 }
1114 if (iPart == 50000050) { // If particle is photon then ...
1115 float etot = fMC->Etot();
1116 float enDep = fMC->Edep();
1117 Int_t parentID = stack->GetCurrentTrack()->GetMother(0);
1118 if (volID == mTOPVolID) {
1119 if (!RegisterPhotoE(etot)) {
1120 fMC->StopTrack();
1121 return kFALSE;
1122 }
1123 mTrackIdTop = trackID;
1124 }
1125
1126 if (volID == mMTOVolID) {
1127 if (trackID != mTrackIdTop) {
1128 if (!RegisterPhotoE(etot)) {
1129 fMC->StopTrack();
1130 return kFALSE;
1131 }
1132 mTrackIdMCPtop = trackID;
1133 }
1134 }
1135
1136 if (volID == mREGVolID) {
1137 if (trackID != mTrackIdTop && trackID != mTrackIdMCPtop) {
1138 if (RegisterPhotoE(etot)) {
1139 AddHit(x, y, z, time, enDep, parentID, detID);
1140 }
1141 }
1142 if (trackID == mTrackIdTop || trackID == mTrackIdMCPtop) {
1143 AddHit(x, y, z, time, enDep, parentID, detID);
1144 }
1145 }
1146 }
1147
1148 return kTRUE;
1149 }
1150 return kFALSE;
1151}
1152
1153o2::ft0::HitType* Detector::AddHit(float x, float y, float z, float time, float energy, Int_t trackId, Int_t detId)
1154{
1155 mHits->emplace_back(x, y, z, time, energy, trackId, detId);
1156 if (energy == 10) {
1157 auto stack = (o2::data::Stack*)fMC->GetStack();
1158 stack->addHit(GetDetId());
1159 }
1160 return &(mHits->back());
1161}
1162
1164{
1165 // This will create a branch in the output tree called Hit, setting the last
1166 // parameter to kFALSE means that this collection will not be written to the file,
1167 // it will exist only during the simulation
1168
1169 if (FairRootManager::Instance()) {
1170 FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE);
1171 }
1172}
1173
1175{
1176 if (!o2::utils::ShmManager::Instance().isOperational()) {
1177 mHits->clear();
1178 }
1179}
1180
1182{
1183 Int_t isxfld = 2; // magneticField->Integ();
1184 Float_t sxmgmx = 10.; // magneticField->Max();
1185 // FIXME: use o2::base::Detector::initFieldTrack to init mag field params
1186
1187 // Float_t a,z,d,radl,absl,buf[1];
1188 // Int_t nbuf;
1189 // AIR
1190
1191 Float_t aAir[4] = {12.0107, 14.0067, 15.9994, 39.948};
1192 Float_t zAir[4] = {6., 7., 8., 18.};
1193 Float_t wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827};
1194 Float_t dAir = 1.20479E-3;
1195 Float_t dAir1 = 1.20479E-11;
1196 // Radiator glass SiO2
1197 Float_t aglass[2] = {28.0855, 15.9994};
1198 Float_t zglass[2] = {14., 8.};
1199 Float_t wglass[2] = {1., 2.};
1200 Float_t dglass = 2.2;
1201 // MCP glass SiO2
1202 Float_t dglass_mcp = 1.3;
1203 /* Ceramic 97.2% Al2O3 , 2.8% SiO2 : average material for
1204 - stack of 2 MCPs thickness 2mm with density 1.6 g/cm3
1205 - back wall of MCP thickness 2 mm with density 2.4 g/cm3
1206 - MCP electrods thickness 1 mm with density 4.2 g/cm3
1207 - Backplane PCBs thickness 4.5 mm with density 1.85 g/cm3
1208 - electromagnetic shielding 1 mm with density 2.8 g/cm3
1209 - Al back cover 5mm 2.7 g/cm3
1210 */
1211 Float_t aCeramic[2] = {26.981539, 15.9994};
1212 Float_t zCeramic[2] = {13., 8.};
1213 Float_t wCeramic[2] = {2., 3.};
1214 Float_t denscer = 2.37;
1215
1216 // MCP walls Ceramic+Nickel (50//50)
1217 const Int_t nCeramicNice = 3;
1218 Float_t aCeramicNicel[3] = {26.981539, 15.9994, 58.6934};
1219 Float_t zCeramicNicel[3] = {13., 8., 28};
1220 Float_t wCeramicNicel[3] = {0.2, 0.3, 0.5};
1221 Float_t denscerCeramicNickel = 5.6;
1222
1223 // Mixed Cables material simulated as plastic with density taken from description of Low Loss Microwave Coax24 AWG 0
1224 // plastic + cooper (6%)
1225 const Int_t nPlast = 4;
1226 Float_t aPlast[nPlast] = {1.00784, 12.0107, 15.999, 63.54};
1227 Float_t zPlast[nPlast] = {1, 6, 8, 29};
1228 Float_t wPlast[nPlast] = {0.08, 0.53, 0.22, 0.17};
1229 const Float_t denCable = 3.66;
1230
1231 // Black paper
1232 // G4Element* elC = new G4Element("Carbon", "C", 6., 12.0107*g/mole);
1233 // G4Material* C = new G4Material("Carbon Material", 3.52*g/cm3, 1);
1234 // C->AddElement(elC, 1);
1235
1236 //*** Definition Of avaible FIT materials ***
1237 Material(11, "Aliminium$", 26.98, 13.0, 2.7, 8.9, 999);
1238 Mixture(1, "Vacuum$", aAir, zAir, dAir1, 4, wAir);
1239 Mixture(2, "Air$", aAir, zAir, dAir, 4, wAir);
1240 Mixture(4, "MCP glass $", aglass, zglass, dglass_mcp, -2, wglass);
1241 Mixture(24, "Radiator Optical glass$", aglass, zglass, dglass, -2, wglass);
1242 Mixture(3, "Ceramic$", aCeramic, zCeramic, denscer, -2, wCeramic);
1243 Mixture(23, "CablePlasticCooper$", aPlast, zPlast, denCable, 4, wPlast);
1244 Mixture(25, "MCPwalls $", aCeramicNicel, zCeramicNicel, denscerCeramicNickel, 3, wCeramicNicel);
1245
1246 Medium(1, "Air$", 2, 0, isxfld, sxmgmx, 10., .1, 1., .003, .003);
1247 Medium(3, "Vacuum$", 1, 0, isxfld, sxmgmx, 10., .01, .1, .003, .003);
1248 Medium(4, "Ceramic$", 3, 0, isxfld, sxmgmx, 10., .01, .1, .003, .003);
1249 Medium(6, "Glass$", 4, 0, isxfld, sxmgmx, 10., .01, .1, .003, .003);
1250 Medium(15, "Aluminium$", 11, 0, isxfld, sxmgmx, 10., .01, 1., .003, .003);
1251 Medium(17, "OptAluminium$", 11, 0, isxfld, sxmgmx, 10., .01, 1., .003, .003);
1252 Medium(16, "OpticalGlass$", 24, 1, isxfld, sxmgmx, 10., .01, .1, .003, .01);
1253 Medium(19, "OpticalGlassCathode$", 24, 1, isxfld, sxmgmx, 10., .01, .1, .003, .003);
1254 // Medium(22, "SensAir$", 2, 1, isxfld, sxmgmx, 10., .1, 1., .003, .003);
1255 Medium(23, "Cables$", 23, 1, isxfld, sxmgmx, 10., .1, 1., .003, .003);
1256 Medium(25, "MCPWalls", 25, 1, isxfld, sxmgmx, 10., .1, 1., .003, .003);
1257}
1258
1259//-------------------------------------------------------------------
1261{
1262 // Path of the optical properties input file
1263 TString inputDir;
1264 const char* aliceO2env = std::getenv("O2_ROOT");
1265 if (aliceO2env) {
1266 inputDir = aliceO2env;
1267 }
1268 inputDir += "/share/Detectors/FT0/files/";
1269
1270 TString optPropPath = inputDir + "quartzOptProperties.txt";
1271 optPropPath = gSystem->ExpandPathName(optPropPath.Data()); // Expand $(ALICE_ROOT) into real system path
1272
1273 Int_t result = ReadOptProperties(optPropPath.Data());
1274 if (result < 0) {
1275 // Error reading file
1276 LOG(error) << "Could not read FIT optical properties " << result << " " << optPropPath.Data();
1277 return;
1278 }
1279 Int_t nBins = mPhotonEnergyD.size();
1280 // set QE
1281 mPMTeff = new TGraph(nBins, &(mPhotonEnergyD[0]), &(mQuantumEfficiency[0]));
1282
1283 // Prepare pointers for arrays with constant and hardcoded values (independent on wavelength)
1285
1286 // Quick conversion from vector<Double_t> to Double_t*: photonEnergyD -> &(photonEnergyD[0])
1287 TVirtualMC::GetMC()->SetCerenkov(getMediumID(kOpGlass), nBins, &(mPhotonEnergyD[0]), &(mAbsorptionLength[0]),
1288 &(mQuantumEfficiency[0]), &(mRefractionIndex[0]));
1289 TVirtualMC::GetMC()->SetCerenkov(getMediumID(kOpGlassCathode), nBins, &(mPhotonEnergyD[0]), &(mAbsorptionLength[0]),
1290 &(mQuantumEfficiency[0]), &(mRefractionIndex[0]));
1291
1292 // Define a side mirror border for radiator optical properties
1293 TVirtualMC::GetMC()->DefineOpSurface("surfRd", kUnified, kDielectric_dielectric, kPolishedbackpainted, 0.);
1294 TVirtualMC::GetMC()->SetMaterialProperty("surfRd", "EFFICIENCY", nBins, &(mPhotonEnergyD[0]), &(mEfficMet[0]));
1295 TVirtualMC::GetMC()->SetMaterialProperty("surfRd", "REFLECTIVITY", nBins, &(mPhotonEnergyD[0]), &(mReflMet[0]));
1296 TVirtualMC::GetMC()->SetBorderSurface("surMirrorBorder0", "0TOP", 1, "0RFV", 1, "surfRd");
1297 TVirtualMC::GetMC()->SetBorderSurface("surMirrorBorder1", "0TOP", 1, "0RFH", 1, "surfRd");
1298 TVirtualMC::GetMC()->SetBorderSurface("surMirrorBorder2", "0TOP", 1, "0RFV", 2, "surfRd");
1299 TVirtualMC::GetMC()->SetBorderSurface("surMirrorBorder3", "0TOP", 1, "0RFH", 2, "surfRd");
1300 // between cathode and back of front MCP glass window
1301 TVirtualMC::GetMC()->DefineOpSurface("surFrontBWindow", kUnified, kDielectric_dielectric, kPolished, 0.);
1302 TVirtualMC::GetMC()->SetMaterialProperty("surFrontBWindow", "EFFICIENCY", nBins, &(mPhotonEnergyD[0]), &(mEffFrontWindow[0]));
1303 TVirtualMC::GetMC()->SetMaterialProperty("surFrontBWindow", "REFLECTIVITY", nBins, &(mPhotonEnergyD[0]), &(mReflFrontWindow[0]));
1304 TVirtualMC::GetMC()->SetBorderSurface("surBorderFrontBWindow", "0REG", 1, "0MTO", 1, "surFrontBWindow");
1305 // between radiator and front MCP glass window
1306 TVirtualMC::GetMC()->DefineOpSurface("surBackFrontWindow", kUnified, kDielectric_dielectric, kPolished, 0.);
1307 TVirtualMC::GetMC()->SetMaterialProperty("surBackFrontWindow", "EFFICIENCY", nBins, &(mPhotonEnergyD[0]), &(mEffFrontWindow[0]));
1308 TVirtualMC::GetMC()->SetMaterialProperty("surBackFrontWindow", "REFLECTIVITY", nBins, &(mPhotonEnergyD[0]), &(mReflFrontWindow[0]));
1309 TVirtualMC::GetMC()->SetBorderSurface("surBorderBackFrontWindow", "0TOP", 1, "0MTO", 1, "surBackFrontWindow");
1310}
1312{
1313 // Set constant values to the other arrays
1314 for (Int_t i = 0; i < mPhotonEnergyD.size(); i++) {
1315 mReflBlackPaper.push_back(0.);
1316 mEffBlackPaper.push_back(0);
1317 mAbsBlackPaper.push_back(1);
1318
1319 mReflFrontWindow.push_back(0.01);
1320 mEffFrontWindow.push_back(1);
1321 mRindexFrontWindow.push_back(1);
1322
1323 mRindexAir.push_back(1.);
1324 mAbsorAir.push_back(0.3);
1325 mRindexCathodeNext.push_back(1);
1326
1327 mAbsorbCathodeNext.push_back(1);
1328 mEfficMet.push_back(0);
1329 mRindexMet.push_back(1);
1330 mReflMet.push_back(1);
1331 }
1332}
1333
1334//------------------------------------------------------------------------
1335Bool_t Detector::RegisterPhotoE(float energy)
1336{
1337 float eff = mPMTeff->Eval(energy);
1338 float p = gRandom->Rndm();
1339 if (p > eff) {
1340 return kFALSE;
1341 }
1342
1343 return kTRUE;
1344}
1345
1346Int_t Detector::ReadOptProperties(const std::string filePath)
1347{
1348 std::ifstream infile;
1349 infile.open(filePath.c_str());
1350 LOG(info) << " file " << filePath.c_str();
1351 // Check if file is opened correctly
1352 if (infile.fail() == true) {
1353 // AliFatal(Form("Error opening ascii file: %s", filePath.c_str()));
1354 return -1;
1355 }
1356
1357 std::string comment; // dummy, used just to read 4 first lines and move the cursor to the 5th, otherwise unused
1358 if (!getline(infile, comment)) { // first comment line
1359 LOG(error) << "Error opening ascii file (it is probably a folder!): " << filePath.c_str();
1360 return -2;
1361 }
1362 getline(infile, comment); // 2nd comment line
1363
1364 // Get number of elements required for the array
1365 Int_t nLines;
1366 infile >> nLines;
1367 if (nLines < 0 || nLines > 1e4) {
1368 return -4;
1369 }
1370
1371 getline(infile, comment); // finish 3rd line after the nEntries are read
1372 getline(infile, comment); // 4th comment line
1373
1374 // read the main body of the file (table of values: energy, absorption length and refractive index)
1375 Int_t iLine = 0;
1376 std::string sLine;
1377 getline(infile, sLine);
1378 while (!infile.eof()) {
1379 if (iLine >= nLines) {
1380 // LOG(error) << "Line number: " << iLine << " reaches range of declared arraySize:" << kNbins << " Check input file:" << filePath.c_str();
1381 return -5;
1382 }
1383 std::stringstream ssLine(sLine);
1384 // First column:
1385 Double_t energy;
1386 ssLine >> energy;
1387 energy *= 1e-9; // Convert eV -> GeV immediately
1388 mPhotonEnergyD.push_back(energy);
1389 // Second column:
1390 Double_t absorption;
1391 ssLine >> absorption;
1392 mAbsorptionLength.push_back(absorption);
1393 // Third column:
1394 Double_t refraction;
1395 ssLine >> refraction;
1396 mRefractionIndex.push_back(refraction);
1397 // Fourth column:
1398 Double_t efficiency;
1399 ssLine >> efficiency;
1400 mQuantumEfficiency.push_back(efficiency);
1401 if (!(ssLine.good() || ssLine.eof())) { // check if there were problems with numbers conversion
1402 // AliFatal(Form("Error while reading line %i: %s", iLine, ssLine.str().c_str()));
1403
1404 return -6;
1405 }
1406 getline(infile, sLine);
1407 iLine++;
1408 }
1409 if (iLine != mPhotonEnergyD.size()) {
1410 // LOG(error)(Form("Total number of lines %i is different than declared %i. Check input file: %s", iLine, kNbins,
1411 // filePath.c_str()));
1412 return -7;
1413 }
1414
1415 LOG(info) << "Optical properties taken from the file: " << filePath.c_str() << " Number of lines read: " << iLine;
1416 return 0;
1417}
1418
1420{
1421 // Path of the LookUp table
1422 std::string inputDir;
1423 const char* aliceO2env = std::getenv("O2_ROOT");
1424 if (aliceO2env) {
1425 inputDir = aliceO2env;
1426 }
1427 inputDir += "/share/Detectors/FT0/files/";
1428
1429 std::string indPath = inputDir + "Sim2DataChannels.txt";
1430 indPath = gSystem->ExpandPathName(indPath.data()); // Expand $(ALICE_ROOT) into real system path
1431
1432 std::ifstream infile;
1433 infile.open(indPath.data());
1434 LOG(info) << " file open " << indPath.data();
1435 // Check if file is opened correctly
1436 if (infile.fail() == true) {
1437 LOG(error) << "Error opening ascii file (it is probably a folder!): " << indPath.c_str();
1438 }
1439 int fromfile;
1440 for (int iind = 0; iind < Geometry::Nchannels; iind++) {
1441 infile >> fromfile;
1442 mSim2LUT[iind] = fromfile;
1443 }
1444}
Definition of the Stack class.
Definition of the Detector class.
ClassImp(Detector)
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
int32_t i
uint32_t stack
Definition RawData.h:1
std::ostringstream debug
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
int getMediumID(int imed) const
Definition Detector.h:135
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 SetOneMCP(TGeoVolume *stl)
Definition Detector.cxx:201
TGeoVolume * SetCablesSize(int mod)
Definition Detector.cxx:327
Int_t ReadOptProperties(const std::string inputFilePath)
void DefineSim2LUTindex()
~Detector() override
Destructor.
Definition Detector.cxx:59
void DefineOpticalProperties()
void InitializeO2Detector() override
Initialization of the detector is done here.
Definition Detector.cxx:64
Bool_t ProcessHits(FairVolume *v) override
This method is called for each step during simulation (see FairMCApplication::Stepping())
void Register() override
void FillOtherOptProperties()
void ConstructGeometry() override
Definition Detector.cxx:91
void addAlignableVolumes() const override
Add alignable volumes.
Definition Detector.cxx:365
void CreateMaterials()
Base class to create the detector geometry.
void SetCablesA(TGeoVolume *stl)
Definition Detector.cxx:290
void ConstructOpGeometry() override
Definition Detector.cxx:192
o2::ft0::HitType * AddHit(float x, float y, float z, float time, float energy, Int_t trackId, Int_t detId)
Detector()=default
Default constructor.
Bool_t RegisterPhotoE(float energy)
void Reset() override
TVector3 centerMCP(int imcp)
Definition Geometry.h:46
static constexpr float ZdetC
Definition Geometry.h:55
static constexpr float ZdetA
Definition Geometry.h:54
static constexpr int NCellsA
Definition Geometry.h:52
static constexpr int Nchannels
Definition Geometry.h:50
static constexpr int NCellsC
Definition Geometry.h:53
TVector3 tiltMCP(int imcp)
Definition Geometry.h:47
static ShmManager & Instance()
Definition ShmManager.h:61
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint64EXT * result
Definition glcorearb.h:5662
const GLdouble * v
Definition glcorearb.h:832
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLint y
Definition glcorearb.h:270
GLboolean * data
Definition glcorearb.h:298
GLsizei const GLint * box
Definition glcorearb.h:4697
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
std::vector< VerticalEdge< T > > sweep(Node< T > *segmentTree, const std::vector< VerticalEdge< T > > &polygonVerticalEdges)
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"