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