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