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