Belle II Software  release-05-01-25
GeoBKLMCreator.cc
1 /*************************************************************************
2 * BASF2 (Belle Analysis Framework 2) *
3 * Copyright(C) 2010 - Belle II Collaboration *
4 * *
5 * Author: The Belle II Collaboration *
6 * Contributors: Leo Piilonen *
7 * *
8 * This software is provided "as is" without any warranty. *
9 *************************************************************************/
10 
11 /* Own header. */
12 #include <klm/bklm/geometry/GeoBKLMCreator.h>
13 
14 /* KLM headers. */
15 #include <klm/bklm/simulation/SensitiveDetector.h>
16 #include <klm/dataobjects/bklm/BKLMElementNumbers.h>
17 #include <klm/dbobjects/bklm/BKLMGeometryPar.h>
18 
19 /* Belle 2 headers. */
20 #include <geometry/Materials.h>
21 
22 /* Geant 4 headers. */
23 #include <G4Box.hh>
24 #include <G4LogicalVolume.hh>
25 #include <G4Polyhedra.hh>
26 #include <G4PVPlacement.hh>
27 #include <G4Region.hh>
28 #include <G4String.hh>
29 #include <G4SubtractionSolid.hh>
30 #include <G4Transform3D.hh>
31 #include <G4Tubs.hh>
32 #include <G4UnionSolid.hh>
33 #include <G4VisAttributes.hh>
34 
35 using namespace std;
36 using namespace Belle2::bklm;
37 using namespace Belle2::geometry;
38 
39 //-----------------------------------------------------------------
40 // Implementation
41 //-----------------------------------------------------------------
42 
43 GeoBKLMCreator::GeoBKLMCreator()
44 {
45  m_Sensitive = dynamic_cast<G4VSensitiveDetector*>(new SensitiveDetector(G4String("BKLM")));
46  m_GeoPar = nullptr;
47  m_SectorDphi = 0.0;
48  m_SectorDz = 0.0;
49  m_RibShift = 0.0;
50  m_CapSolid = nullptr;
51  m_CapLogical[0] = m_CapLogical[1] = nullptr;
52  m_InnerIronSolid = nullptr;
53  m_InnerIronLogical[0] = m_InnerIronLogical[1] = m_InnerIronLogical[2] = m_InnerIronLogical[3] = nullptr;
54  m_InnerAirSolid = nullptr;
55  m_InnerAirLogical[0] = m_InnerAirLogical[1] = m_InnerAirLogical[2] = m_InnerAirLogical[3] = nullptr;
56  m_SupportLogical[0] = m_SupportLogical[1] = nullptr;
57  m_BracketLogical = nullptr;
58  for (int j = 0; j < BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
59  m_LayerIronSolid[j] = nullptr;
60  }
61  for (int j = 0; j < 2 * BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
62  m_LayerModuleLogical[j] = nullptr;
63  m_LayerGapSolid[j] = nullptr;
64  }
65  for (int j = 0; j < 12 * BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
66  m_LayerIronLogical[j] = nullptr;
67  m_LayerGapLogical[j] = nullptr;
68  }
69  m_SectorTube = nullptr;
70  for (int sector = 0; sector < BKLMElementNumbers::getMaximalSectorNumber(); ++sector) {
71  m_SectorLogical[0][sector] = nullptr;
72  m_SectorLogical[1][sector] = nullptr;
73  }
74  m_MPPCHousingLogical = nullptr;
75  m_ReadoutContainerLogical = nullptr;
76  m_SolenoidTube = nullptr;
77  m_ScintLogicals.clear();
78  m_VisAttributes.clear();
79  m_VisAttributes.push_back(new G4VisAttributes(false)); // for "invisible"
80  m_Names.clear();
81 }
82 
83 GeoBKLMCreator::~GeoBKLMCreator()
84 {
85  delete m_Sensitive;
86  m_ScintLogicals.clear();
87  for (G4VisAttributes* visAttr : m_VisAttributes) delete visAttr;
88  m_VisAttributes.clear();
89  for (G4String* name : m_Names) delete name;
90  m_Names.clear();
91 }
92 
93 //-----------------------------------------------------------------
94 // Build and place the BKLM
95 //-----------------------------------------------------------------
96 
97 void GeoBKLMCreator::createGeometry(const BKLMGeometryPar& parameters, G4LogicalVolume& motherLogical, GeometryTypes)
98 {
99 
100  m_GeoPar = GeometryPar::instance(parameters);
101  m_SectorDphi = 2.0 * M_PI / m_GeoPar->getNSector();
102  m_SectorDz = 0.5 * m_GeoPar->getHalfLength() * CLHEP::cm;
103  m_RibShift = 0.5 * m_GeoPar->getRibThickness() * CLHEP::cm / sin(0.5 * m_SectorDphi);
104 
105  // Place BKLM envelope in mother volume
106  G4Tubs* envelopeSolid =
107  new G4Tubs("BKLM.EnvelopeSolid",
108  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
109  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
110  2.0 * m_SectorDz,
111  0.0,
112  2.0 * M_PI
113  );
114  G4LogicalVolume* envelopeLogical =
115  new G4LogicalVolume(envelopeSolid,
116  Materials::get("G4_AIR"),
117  "BKLM.EnvelopeLogical"
118  );
119  envelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
120  putEndsInEnvelope(envelopeLogical);
121  /* Set up region for production cuts. */
122  G4Region* aRegion = new G4Region("BKLMEnvelope");
123  envelopeLogical->SetRegion(aRegion);
124  aRegion->AddRootLogicalVolume(envelopeLogical);
125 
126  new G4PVPlacement(G4TranslateZ3D(m_GeoPar->getOffsetZ() * CLHEP::cm) * G4RotateZ3D(m_GeoPar->getRotation() * CLHEP::rad),
127  envelopeLogical,
128  "BKLM.EnvelopePhysical",
129  &motherLogical,
130  false,
131  1,
132  false
133  );
134 
135 }
136 
137 void GeoBKLMCreator::putEndsInEnvelope(G4LogicalVolume* envelopeLogical)
138 {
139  G4Tubs* endSolid =
140  new G4Tubs("BKLM.EndSolid",
141  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
142  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
143  m_SectorDz,
144  0.0,
145  2.0 * M_PI
146  );
147  G4LogicalVolume* frontLogical =
148  new G4LogicalVolume(endSolid,
149  Materials::get("G4_AIR"),
150  "BKLM.F_Logical"
151  );
152  frontLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
153  putSectorsInEnd(frontLogical, BKLMElementNumbers::c_ForwardSection);
154  new G4PVPlacement(G4TranslateZ3D(m_SectorDz),
155  frontLogical,
156  "BKLM.F_Physical",
157  envelopeLogical,
158  false,
159  BKLMElementNumbers::c_ForwardSection,
160  false
161  );
162  G4LogicalVolume* backLogical =
163  new G4LogicalVolume(endSolid,
164  Materials::get("G4_AIR"),
165  "BKLM.B_Logical"
166  );
167  backLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
168  putSectorsInEnd(backLogical, BKLMElementNumbers::c_BackwardSection);
169  new G4PVPlacement(G4TranslateZ3D(-m_SectorDz) * G4RotateY3D(M_PI),
170  backLogical,
171  "BKLM.B_Physical",
172  envelopeLogical,
173  false,
174  BKLMElementNumbers::c_BackwardSection,
175  false
176  );
177 
178 }
179 
180 void GeoBKLMCreator::putSectorsInEnd(G4LogicalVolume* endLogical, int section)
181 {
182  if (m_SectorTube == nullptr) {
183  m_SectorTube =
184  new G4Tubs("BKLM.SectorSolid",
185  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
186  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
187  m_SectorDz,
188  -0.5 * m_SectorDphi,
189  m_SectorDphi
190  );
191  }
192  char name[80] = "";
193  for (int s = 0; s < m_GeoPar->getNSector(); ++s) {
194  int sector = (section == BKLMElementNumbers::c_ForwardSection ? s : ((12 - s) % 8)) + 1;
195  bool hasChimney = (section == BKLMElementNumbers::c_BackwardSection) && (sector == BKLMElementNumbers::c_ChimneySector);
196  bool hasInnerSupport = (sector <= m_GeoPar->getNSector() / 2 + 1);
197  sprintf(name, "BKLM.%sSector%dLogical", (section == BKLMElementNumbers::c_ForwardSection ? "Forward" : "Backward"), sector);
198  m_SectorLogical[section][sector - 1] =
199  new G4LogicalVolume(m_SectorTube,
200  Materials::get("G4_AIR"),
201  name
202  );
203  m_SectorLogical[section][sector - 1]->SetVisAttributes(m_VisAttributes.front()); // invisible
204  putCapInSector(m_SectorLogical[section][sector - 1], hasChimney);
205  putInnerRegionInSector(m_SectorLogical[section][sector - 1], hasInnerSupport, hasChimney);
206  putLayersInSector(m_SectorLogical[section][sector - 1], section, sector, hasChimney);
207  new G4PVPlacement(G4RotateZ3D(m_SectorDphi * s),
208  m_SectorLogical[section][sector - 1],
209  physicalName(m_SectorLogical[section][sector - 1]),
210  endLogical,
211  false,
212  sector,
213  false
214  );
215  }
216 }
217 
218 void GeoBKLMCreator::putCapInSector(G4LogicalVolume* sectorLogical, bool hasChimney)
219 {
220 
221  // Fill cap with iron and (aluminum) cables
222  const CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(0, false) * CLHEP::cm;
223  const double dyBrace = (hasChimney ? m_GeoPar->getBraceWidthChimney() : m_GeoPar->getBraceWidth()) * CLHEP::cm;
224  const double dy = 0.25 * (m_GeoPar->getCablesWidth() * CLHEP::cm - dyBrace);
225  const double dz = m_SectorDz - gapHalfSize.z();
226  const double ri = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm + 2.0 * gapHalfSize.x();
227  const double ro = m_GeoPar->getOuterRadius() * CLHEP::cm;
228  if (m_CapSolid == nullptr) {
229  const double z[2] = { -dz, dz};
230  const double rInner[2] = {ri, ri};
231  const double rOuter[2] = {ro, ro};
232  m_CapSolid =
233  new G4Polyhedra("BKLM.CapSolid",
234  -0.5 * m_SectorDphi,
235  m_SectorDphi,
236  1,
237  2, z, rInner, rOuter
238  );
239  }
240  int newLvol = (hasChimney ? 1 : 0);
241  G4String name = (hasChimney ? "BKLM.ChimneyCapLogical" : "BKLM.CapLogical");
242  if (m_CapLogical[newLvol] == nullptr) {
243  m_CapLogical[newLvol] =
244  new G4LogicalVolume(m_CapSolid,
245  Materials::get("G4_Fe"),
246  name
247  );
248  m_CapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
249  name = (hasChimney ? "BKLM.ChimneyCablesSolid" : "BKLM.CablesSolid");
250  G4Box* cablesSolid =
251  new G4Box(name, 0.5 * (ro - ri), dy, dz);
252  G4LogicalVolume* cablesLogical =
253  new G4LogicalVolume(cablesSolid,
254  Materials::get("G4_Al"),
255  logicalName(cablesSolid)
256  );
257  cablesLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
258  new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), -(0.5 * dyBrace + dy), 0.0),
259  cablesLogical,
260  physicalName(cablesLogical).append(G4String("_L")),
261  m_CapLogical[newLvol],
262  false,
263  1,
264  false
265  );
266  new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), +(0.5 * dyBrace + dy), 0.0),
267  cablesLogical,
268  physicalName(cablesLogical).append(G4String("_R")),
269  m_CapLogical[newLvol],
270  false,
271  2,
272  false
273  );
274  }
275  new G4PVPlacement(G4TranslateZ3D(m_SectorDz - dz),
276  m_CapLogical[newLvol],
277  physicalName(m_CapLogical[newLvol]),
278  sectorLogical,
279  false,
280  1,
281  false
282  );
283 }
284 
285 void GeoBKLMCreator::putInnerRegionInSector(G4LogicalVolume* sectorLogical, bool hasInnerSupport, bool hasChimney)
286 {
287 
288  // Fill inner region with iron
289  if (m_InnerIronSolid == nullptr) {
290  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm;
291  const double z[2] = { -m_SectorDz, +m_SectorDz};
292  const double rInner[2] = {0.0, 0.0};
293  const double rOuter[2] = {r, r};
294  G4Polyhedra* innerIronPolygon =
295  new G4Polyhedra("BKLM.InnerIronPolygon",
296  -0.5 * m_SectorDphi,
297  m_SectorDphi,
298  1,
299  2, z, rInner, rOuter
300  );
301  m_InnerIronSolid =
302  new G4SubtractionSolid("BKLM.InnerIronSolid",
303  innerIronPolygon,
304  getSolenoidTube()
305  );
306  }
307  int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
308  if (m_InnerIronLogical[newLvol] == nullptr) {
309  m_InnerIronLogical[newLvol] =
310  new G4LogicalVolume(m_InnerIronSolid,
311  Materials::get("G4_Fe"),
312  (hasChimney ? "BKLM.InnerIronChimneyLogical" : "BKLM.InnerIronLogical")
313  );
314  m_InnerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
315  putVoidInInnerRegion(m_InnerIronLogical[newLvol], hasInnerSupport, hasChimney);
316  }
317  new G4PVPlacement(G4TranslateZ3D(0.0),
318  m_InnerIronLogical[newLvol],
319  physicalName(m_InnerIronLogical[newLvol]),
320  sectorLogical,
321  false,
322  1,
323  false
324  );
325 }
326 
327 void GeoBKLMCreator::putVoidInInnerRegion(G4LogicalVolume* innerIronLogical, bool hasInnerSupport, bool hasChimney)
328 {
329 
330  // Carve out an air void from the inner-region iron, leaving only the ribs
331  // at the azimuthal edges
332  if (m_InnerAirSolid == nullptr) {
333  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - m_RibShift;
334  const double z[2] = { -m_SectorDz, +m_SectorDz};
335  const double rInner[2] = {0.0, 0.0};
336  const double rOuter[2] = {r, r};
337  G4Polyhedra* innerAirPolygon =
338  new G4Polyhedra("BKLM.InnerAirPolygon",
339  -0.5 * m_SectorDphi,
340  m_SectorDphi,
341  1,
342  2, z, rInner, rOuter
343  );
344  m_InnerAirSolid =
345  new G4SubtractionSolid("BKLM.InnerAirSolid",
346  innerAirPolygon,
347  getSolenoidTube(),
348  G4TranslateX3D(-m_RibShift)
349  );
350  }
351  int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
352  if (m_InnerAirLogical[newLvol] == nullptr) {
353  m_InnerAirLogical[newLvol] =
354  new G4LogicalVolume(m_InnerAirSolid,
355  Materials::get("G4_AIR"),
356  (hasChimney ? "BKLM.InnerAirChimneyLogical" : "BKLM.InnerAirLogical")
357  );
358  m_InnerAirLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
359  if (hasInnerSupport) {
360  putLayer1SupportInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
361  putLayer1BracketsInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
362  }
363  }
364  new G4PVPlacement(G4TranslateX3D(m_RibShift),
365  m_InnerAirLogical[newLvol],
366  physicalName(m_InnerAirLogical[newLvol]),
367  innerIronLogical,
368  false,
369  1,
370  false
371  );
372 }
373 
374 void GeoBKLMCreator::putLayer1SupportInInnerVoid(G4LogicalVolume* innerAirLogical, bool hasChimney)
375 {
376 
377  int newLvol = (hasChimney ? 1 : 0);
378  const CLHEP::Hep3Vector size = m_GeoPar->getSupportPlateHalfSize(hasChimney) * CLHEP::cm;
379  if (m_SupportLogical[newLvol] == nullptr) {
380  G4Box* supportBox =
381  new G4Box((hasChimney ? "BKLM.ChimneySupportSolid" : "BKLM.SupportSolid"),
382  size.x(),
383  size.y(),
384  size.z()
385  );
386  m_SupportLogical[newLvol] =
387  new G4LogicalVolume(supportBox,
388  Materials::get("G4_Al"),
389  logicalName(supportBox)
390  );
391  m_SupportLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
392  }
393  double dx = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - size.x() - m_RibShift;
394  double dz = size.z() - m_SectorDz;
395  new G4PVPlacement(G4Translate3D(dx, 0.0, dz),
396  m_SupportLogical[newLvol],
397  physicalName(m_SupportLogical[newLvol]),
398  innerAirLogical,
399  false,
400  1,
401  false
402  );
403 }
404 
405 void GeoBKLMCreator::putLayer1BracketsInInnerVoid(G4LogicalVolume* innerAirLogical, bool hasChimney)
406 {
407 
408  if (m_BracketLogical == nullptr) {
409  const CLHEP::Hep3Vector size = m_GeoPar->getSupportPlateHalfSize(hasChimney) * CLHEP::cm;
410  const double dz = 0.5 * m_GeoPar->getBracketLength() * CLHEP::cm;
411  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - m_RibShift - 2.0 * size.x();
412  const double bracketShift = m_GeoPar->getBracketRibThickness() * CLHEP::cm / sin(0.5 * m_SectorDphi);
413  const double z[2] = { -dz, +dz};
414  const double rInner[2] = {0.0, 0.0};
415  const double rOuter[2] = {r, r};
416  const double r1 = m_GeoPar->getBracketInnerRadius() * CLHEP::cm - m_RibShift;
417  const double z1[2] = { -(dz + 0.5 * CLHEP::cm), dz + 0.5 * CLHEP::cm};
418  const double rOuter1[2] = {r1, r1};
419  const double z2[2] = { -(dz + 1.0 * CLHEP::cm), dz + 1.0 * CLHEP::cm};
420  const double dzBracket = m_GeoPar->getBracketThickness() * CLHEP::cm;
421  const double drBracket = dzBracket + bracketShift;
422  const double rOuter2[2] = {r - drBracket, r - drBracket};
423  const double z3[2] = { -dzBracket, dzBracket};
424  const double cutoutDphi = m_GeoPar->getBracketCutoutDphi() * CLHEP::rad;
425  G4Polyhedra* bracketPolygon =
426  new G4Polyhedra("BKLM.BracketPolygon",
427  -0.5 * m_SectorDphi,
428  m_SectorDphi,
429  1,
430  2, z, rInner, rOuter
431  );
432  G4Polyhedra* bracketCutout1 =
433  new G4Polyhedra("BKLM.BracketCutout1",
434  -0.5 * cutoutDphi,
435  cutoutDphi,
436  2, // two sides
437  2, z1, rInner, rOuter1
438  );
439  G4Polyhedra* bracketCutout2 =
440  new G4Polyhedra("BKLM.BracketCutout2",
441  -0.5 * m_SectorDphi,
442  m_SectorDphi,
443  1,
444  2, z2, rInner, rOuter2
445  );
446  G4Polyhedra* bracketCutout3 =
447  new G4Polyhedra("BKLM.BracketCutout3",
448  -0.5 * m_SectorDphi,
449  m_SectorDphi,
450  1,
451  2, z3, rInner, rOuter2
452  );
453  G4Box* bracketCutout4 =
454  new G4Box("BKLM.BracketCutout4",
455  rOuter[1] + 1.0 * CLHEP::cm,
456  rOuter[1] * tan(0.5 * m_SectorDphi) - m_GeoPar->getBracketWidth() * CLHEP::cm,
457  z[1] + 1.5 * CLHEP::cm
458  );
459  G4VSolid* polygon1 =
460  new G4SubtractionSolid("BKLM.BracketPolygon1",
461  bracketPolygon,
462  bracketCutout4
463  );
464  G4VSolid* polygon2 =
465  new G4SubtractionSolid("BKLM.BracketPolygon2",
466  bracketCutout2,
467  bracketCutout3
468  );
469  G4VSolid* polygon3 =
470  new G4SubtractionSolid("BKLM.BracketPolygon3",
471  polygon1,
472  polygon2,
473  G4TranslateX3D(bracketShift)
474  );
475  G4VSolid* bracketSolid =
476  new G4SubtractionSolid("BKLM.BracketSolid",
477  polygon3,
478  bracketCutout1
479  );
480  m_BracketLogical =
481  new G4LogicalVolume(bracketSolid,
482  Materials::get("G4_Al"),
483  "BKLM.BracketLogical"
484  );
485  m_VisAttributes.push_back(new G4VisAttributes(true));
486  m_VisAttributes.back()->SetColour(0.6, 0.6, 0.6);
487  m_BracketLogical->SetVisAttributes(m_VisAttributes.back());
488  }
489  char name[80] = "";
490  for (int bracket = 0; bracket < (hasChimney ? 2 : 3); ++bracket) {
491  sprintf(name, "BKLM.Bracket%d%sPhysical", bracket, (hasChimney ? "Chimney" : ""));
492  new G4PVPlacement(G4TranslateZ3D(m_GeoPar->getBracketZPosition(bracket, hasChimney) * CLHEP::cm),
493  m_BracketLogical,
494  name,
495  innerAirLogical,
496  false,
497  bracket,
498  false
499  );
500  }
501 }
502 
503 void GeoBKLMCreator::putLayersInSector(G4LogicalVolume* sectorLogical, int section, int sector, bool hasChimney)
504 {
505 
506  const double dz = 0.5 * m_GeoPar->getGapLength() * CLHEP::cm;
507  const double z[2] = { -dz, +dz };
508  char name[80] = "";
509  for (int layer = 1; layer <= m_GeoPar->getNLayer(); ++layer) {
510  // Fill layer with iron
511  if (m_LayerIronSolid[layer - 1] == nullptr) {
512  const double ri = m_GeoPar->getLayerInnerRadius(layer) * CLHEP::cm;
513  const double ro = m_GeoPar->getLayerOuterRadius(layer) * CLHEP::cm;
514  const double rInner[2] = {ri, ri};
515  const double rOuter[2] = {ro, ro};
516  sprintf(name, "BKLM.Layer%02dIronSolid", layer);
517  m_LayerIronSolid[layer - 1] =
518  new G4Polyhedra(name,
519  -0.5 * m_SectorDphi,
520  m_SectorDphi,
521  1,
522  2, z, rInner, rOuter
523  );
524  }
525  const Module* module = m_GeoPar->findModule(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
526  bool isFlipped = module->isFlipped();
527  int newLvol = BKLMElementNumbers::getMaximalLayerNumber() * ((isFlipped ? 2 : 0) +
528  (hasChimney ? 1 : 0)) + (layer - 1);
529  int s1 = sector - 1;
530  int s2 = m_GeoPar->getNSector() / 2;
531  if (s1 % s2 == 0) {
532  } else if (s1 > s2) {
533  newLvol += BKLMElementNumbers::getMaximalLayerNumber() * 4;
534  } else {
535  newLvol += BKLMElementNumbers::getMaximalLayerNumber() * 8;
536  }
537  if (m_LayerIronLogical[newLvol] == nullptr) {
538  sprintf(name, "BKLM.Layer%02d%s%sIronLogical", layer, (isFlipped ? "Flipped" : ""), (hasChimney ? "Chimney" : ""));
539  m_LayerIronLogical[newLvol] =
540  new G4LogicalVolume(m_LayerIronSolid[layer - 1],
541  Materials::get("G4_Fe"),
542  name
543  );
544  m_LayerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
545  putModuleInLayer(m_LayerIronLogical[newLvol], section, sector, layer, hasChimney, isFlipped, newLvol);
546  if (hasChimney) {
547  putChimneyInLayer(m_LayerIronLogical[newLvol], layer);
548  }
549  }
550  new G4PVPlacement(G4TranslateZ3D(dz - m_SectorDz),
551  m_LayerIronLogical[newLvol],
552  physicalName(m_LayerIronLogical[newLvol]),
553  sectorLogical,
554  false,
555  layer,
556  false
557  );
558 
559  }
560 }
561 
562 void GeoBKLMCreator::putChimneyInLayer(G4LogicalVolume* layerIronLogical, int layer)
563 {
564  CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(layer, true) * CLHEP::cm;
565  CLHEP::Hep3Vector chimneyHalfSize = m_GeoPar->getChimneyHalfSize(layer) * CLHEP::cm;
566  CLHEP::Hep3Vector chimneyPosition = m_GeoPar->getChimneyPosition(layer) * CLHEP::cm;
567  gapHalfSize.setY(0.5 * (gapHalfSize.y() - chimneyHalfSize.y()));
568  gapHalfSize.setZ(chimneyHalfSize.z() + 0.5 * m_GeoPar->getChimneyCoverThickness() * CLHEP::cm);
569  double dx = m_GeoPar->getGapMiddleRadius(layer) * CLHEP::cm;
570  double dy = gapHalfSize.y() + chimneyHalfSize.y();
571  double dz = 0.5 * m_GeoPar->getGapLength() * CLHEP::cm - gapHalfSize.z();
572  char name[80] = "";
573  // Fill the two chimney gaps with air
574  sprintf(name, "BKLM.Layer%02dGapChimneyBox", layer);
575  G4Box* gapBox =
576  new G4Box(name,
577  gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
578  );
579  sprintf(name, "BKLM.Layer%02dGapChimneyLogical", layer);
580  G4LogicalVolume* gapLogical =
581  new G4LogicalVolume(gapBox,
582  Materials::get("G4_AIR"),
583  name
584  );
585  gapLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
586  sprintf(name, "BKLM.Layer%02dLeftGapChimneyPhysical", layer);
587  new G4PVPlacement(G4Translate3D(dx, -dy, dz),
588  gapLogical,
589  name,
590  layerIronLogical,
591  false,
592  0,
593  false
594  );
595  sprintf(name, "BKLM.Layer%02dRightGapChimneyPhysical", layer);
596  new G4PVPlacement(G4Translate3D(dx, +dy, dz),
597  gapLogical,
598  name,
599  layerIronLogical,
600  false,
601  1,
602  false
603  );
604  // Fill chimney with air
605  sprintf(name, "BKLM.Layer%02dChimneyBox", layer);
606  G4Box* chimneyBox =
607  new G4Box(name,
608  chimneyHalfSize.x(), chimneyHalfSize.y(), chimneyHalfSize.z()
609  );
610  sprintf(name, "BKLM.Layer%02dChimneyLogical", layer);
611  G4LogicalVolume* chimneyLogical =
612  new G4LogicalVolume(chimneyBox,
613  Materials::get("G4_AIR"),
614  name
615  );
616  chimneyLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
617  // Place coaxial tubes in chimney
618  G4Tubs* housingTube =
619  new G4Tubs("BKLM.ChimneyHousingTube",
620  m_GeoPar->getChimneyHousingInnerRadius() * CLHEP::cm,
621  m_GeoPar->getChimneyHousingOuterRadius() * CLHEP::cm,
622  chimneyHalfSize.x(),
623  0.0,
624  2.0 * M_PI
625  );
626  G4LogicalVolume* housingLogical =
627  new G4LogicalVolume(housingTube,
628  Materials::get("G4_Fe"),
629  "BKLM.ChimneyHousingLogical"
630  );
631  m_VisAttributes.push_back(new G4VisAttributes(true));
632  m_VisAttributes.back()->SetColour(0.4, 0.4, 0.4);
633  housingLogical->SetVisAttributes(m_VisAttributes.back());
634  new G4PVPlacement(G4RotateY3D(M_PI_2),
635  housingLogical,
636  "BKLM.ChimneyHousingPhysical",
637  chimneyLogical,
638  false,
639  0,
640  false
641  );
642  G4Tubs* shieldTube =
643  new G4Tubs("BKLM.ChimneyShieldTube",
644  m_GeoPar->getChimneyShieldInnerRadius() * CLHEP::cm,
645  m_GeoPar->getChimneyShieldOuterRadius() * CLHEP::cm,
646  chimneyHalfSize.x(),
647  0.0,
648  2.0 * M_PI
649  );
650  G4LogicalVolume* shieldLogical =
651  new G4LogicalVolume(shieldTube,
652  Materials::get("G4_Fe"),
653  "BKLM.ChimneyShieldLogical"
654  );
655  shieldLogical->SetVisAttributes(m_VisAttributes.back());
656  new G4PVPlacement(G4RotateY3D(M_PI_2),
657  shieldLogical,
658  "BKLM.ChimneyShieldPhysical",
659  chimneyLogical,
660  false,
661  0,
662  false
663  );
664  G4Tubs* pipeTube =
665  new G4Tubs("BKLM.ChimneyPipeTube",
666  m_GeoPar->getChimneyPipeInnerRadius() * CLHEP::cm,
667  m_GeoPar->getChimneyPipeOuterRadius() * CLHEP::cm,
668  chimneyHalfSize.x(),
669  0.0,
670  2.0 * M_PI
671  );
672  G4LogicalVolume* pipeLogical =
673  new G4LogicalVolume(pipeTube,
674  Materials::get("G4_Fe"),
675  "BKLM.ChimneyPipeLogical"
676  );
677  m_VisAttributes.push_back(new G4VisAttributes(true));
678  m_VisAttributes.back()->SetColour(0.6, 0.6, 0.6);
679  pipeLogical->SetVisAttributes(m_VisAttributes.back());
680  new G4PVPlacement(G4RotateY3D(M_PI_2),
681  pipeLogical,
682  "BKLM.ChimneyPipePhysical",
683  chimneyLogical,
684  false,
685  0,
686  false
687  );
688  sprintf(name, "BKLM.Layer%02dChimneyPhysical", layer);
689  new G4PVPlacement(G4Translate3D(chimneyPosition),
690  chimneyLogical,
691  name,
692  layerIronLogical,
693  false,
694  0,
695  false
696  );
697 }
698 
699 void GeoBKLMCreator::putModuleInLayer(G4LogicalVolume* layerIronLogical, int section, int sector, int layer, bool hasChimney,
700  bool isFlipped, int newLvol)
701 {
702  const CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(layer, hasChimney) * CLHEP::cm;
703  const CLHEP::Hep3Vector moduleHalfSize = m_GeoPar->getModuleHalfSize(layer, hasChimney) * CLHEP::cm;
704  char name[80] = "";
705  // Fill gap with air
706  int modLvol = (hasChimney ? BKLMElementNumbers::getMaximalLayerNumber() : 0) + (layer - 1);
707  if (m_LayerModuleLogical[modLvol] == nullptr) {
708  // Module is aluminum (but interior will be filled)
709  sprintf(name, "BKLM.Layer%02d%sModuleSolid", layer, (hasChimney ? "Chimney" : ""));
710  G4Box* moduleBox =
711  new G4Box(name,
712  moduleHalfSize.x(), moduleHalfSize.y(), moduleHalfSize.z()
713  );
714  m_LayerModuleLogical[modLvol] =
715  new G4LogicalVolume(moduleBox,
716  Materials::get("G4_Al"),
717  logicalName(moduleBox)
718  );
719  m_LayerModuleLogical[modLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
720  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid1", layer, (hasChimney ? "Chimney" : ""));
721  const CLHEP::Hep3Vector interiorHalfSize1 = m_GeoPar->getModuleInteriorHalfSize1(layer, hasChimney) * CLHEP::cm;
722  G4Box* interiorBox1 =
723  new G4Box(name,
724  interiorHalfSize1.x(), interiorHalfSize1.y(), interiorHalfSize1.z()
725  );
726  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid2", layer, (hasChimney ? "Chimney" : ""));
727  const CLHEP::Hep3Vector interiorHalfSize2 = m_GeoPar->getModuleInteriorHalfSize2(layer, hasChimney) * CLHEP::cm;
728  G4Box* interiorBox2 =
729  new G4Box(name,
730  interiorHalfSize2.x(), interiorHalfSize2.y(), interiorHalfSize2.z()
731  );
732  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid", layer, (hasChimney ? "Chimney" : ""));
733  G4UnionSolid* interiorUnion =
734  new G4UnionSolid(name, interiorBox1, interiorBox2);
735  G4LogicalVolume* interiorLogical =
736  new G4LogicalVolume(interiorUnion,
737  Materials::get((m_GeoPar->hasRPCs(layer) ? "RPCReadout" : "G4_POLYSTYRENE")),
738  logicalName(interiorUnion)
739  );
740  interiorLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
741  if (m_GeoPar->hasRPCs(layer)) {
742  putRPCsInInterior(interiorLogical, layer, hasChimney);
743  } else {
744  putScintsInInterior(interiorLogical, section, sector, layer, hasChimney);
745  }
746  new G4PVPlacement(G4TranslateZ3D(0.0),
747  interiorLogical,
748  physicalName(interiorLogical),
749  m_LayerModuleLogical[modLvol],
750  false,
751  0,
752  false
753  );
754  sprintf(name, "BKLM.Layer%02d%sGapSolid", layer, (hasChimney ? "Chimney" : ""));
755  m_LayerGapSolid[modLvol] =
756  new G4Box(name,
757  gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
758  );
759  }
760  if (m_LayerGapLogical[newLvol] == nullptr) {
761  sprintf(name, "BKLM.Layer%02d%s%sGapLogical", layer, (isFlipped ? "Flipped" : ""), (hasChimney ? "Chimney" : ""));
762  m_LayerGapLogical[newLvol] =
763  new G4LogicalVolume(m_LayerGapSolid[modLvol],
764  Materials::get("G4_AIR"),
765  name
766  );
767  m_LayerGapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
768  }
769  double dx = (m_GeoPar->getModuleMiddleRadius(layer) - m_GeoPar->getGapMiddleRadius(layer)) * CLHEP::cm;
770  // Module is closer to IP within gap if in the upper octants, farther from IP within gap if in the lower octants,
771  // or in the middle of the gap if in the side octants.
772  int s1 = sector - 1;
773  int s2 = m_GeoPar->getNSector() / 2;
774  if (s1 % s2 == 0) {
775  dx = 0.0;
776  } else if (s1 > s2) {
777  dx = -dx;
778  }
779  double dz = moduleHalfSize.z() - gapHalfSize.z();
780  G4Transform3D displacedGeo = m_GeoPar->getModuleDisplacedGeo(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
781  new G4PVPlacement(G4Translate3D(dx, 0.0, dz) * G4RotateZ3D(isFlipped ? M_PI : 0.0) * displacedGeo,
782  m_LayerModuleLogical[modLvol],
783  physicalName(m_LayerModuleLogical[modLvol]),
784  m_LayerGapLogical[newLvol],
785  false,
786  0,
787  false
788  );
789  dz = gapHalfSize.z() - 0.5 * m_GeoPar->getGapLength() * CLHEP::cm;
790  new G4PVPlacement(G4Translate3D(m_GeoPar->getGapMiddleRadius(layer) * CLHEP::cm, 0.0, dz),
791  m_LayerGapLogical[newLvol],
792  physicalName(m_LayerGapLogical[newLvol]),
793  layerIronLogical,
794  false,
795  layer,
796  false
797  );
798 }
799 
800 void GeoBKLMCreator::putRPCsInInterior(G4LogicalVolume* interiorLogical, int layer, bool hasChimney)
801 {
802  char name[80] = "";
803  // Place electrode inside the module's interior
804  sprintf(name, "BKLM.Layer%02d%sElectrodeSolid", layer, (hasChimney ? "Chimney" : ""));
805  const CLHEP::Hep3Vector electrodeHalfSize = m_GeoPar->getElectrodeHalfSize(layer, hasChimney) * CLHEP::cm;
806  G4Box* electrodeBox =
807  new G4Box(name,
808  electrodeHalfSize.x(), electrodeHalfSize.y(), electrodeHalfSize.z()
809  );
810  G4LogicalVolume* electrodeLogical =
811  new G4LogicalVolume(electrodeBox,
812  Materials::get("G4_GLASS_PLATE"),
813  logicalName(electrodeBox)
814  );
815  electrodeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
816  // Place two gas volumes inside electrodes
817  sprintf(name, "BKLM.Layer%02d%sGasSolid", layer, (hasChimney ? "Chimney" : ""));
818  const CLHEP::Hep3Vector gasHalfSize = m_GeoPar->getGasHalfSize(layer, hasChimney) * CLHEP::cm;
819  G4Box* gasBox =
820  new G4Box(name,
821  gasHalfSize.x(), gasHalfSize.y(), gasHalfSize.z()
822  );
823  G4LogicalVolume* gasLogical =
824  new G4LogicalVolume(gasBox,
825  Materials::get("RPCGas"),
826  logicalName(gasBox),
827  0, // use global field manager
828  m_Sensitive, // this is the only sensitive volume in BKLM
829  0 // no user limits
830  );
831  m_VisAttributes.push_back(new G4VisAttributes(true));
832  m_VisAttributes.back()->SetColour(1.0, 0.5, 0.0);
833  gasLogical->SetVisAttributes(m_VisAttributes.back());
834  new G4PVPlacement(G4TranslateX3D(-0.5 * electrodeHalfSize.x()),
835  gasLogical,
836  physicalName(gasLogical),
837  electrodeLogical,
838  false,
839  BKLM_INNER,
840  false
841  );
842 
843  new G4PVPlacement(G4TranslateX3D(+0.5 * electrodeHalfSize.x()),
844  gasLogical,
845  physicalName(gasLogical),
846  electrodeLogical,
847  false,
848  BKLM_OUTER,
849  false
850  );
851  new G4PVPlacement(G4TranslateZ3D(0.0),
852  electrodeLogical,
853  physicalName(electrodeLogical),
854  interiorLogical,
855  false,
856  1,
857  false
858  );
859 }
860 
861 void GeoBKLMCreator::putScintsInInterior(G4LogicalVolume* interiorLogical, int section, int sector, int layer, bool hasChimney)
862 {
863  char name[80] = "";
864  sprintf(name, "BKLM.Layer%02d%sAirSolid", layer, (hasChimney ? "Chimney" : ""));
865  const CLHEP::Hep3Vector airHalfSize = m_GeoPar->getAirHalfSize(layer, hasChimney) * CLHEP::cm;
866  G4Box* airBox =
867  new G4Box(name,
868  airHalfSize.x(), airHalfSize.y(), airHalfSize.z()
869  );
870  G4LogicalVolume* airLogical =
871  new G4LogicalVolume(airBox,
872  Materials::get("G4_AIR"),
873  logicalName(airBox)
874  );
875  airLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
876  sprintf(name, "BKLM.Layer%02d%sScintEnvelopeSolid", layer, (hasChimney ? "Chimney" : ""));
877  double mppcHousingHalfLength = m_GeoPar->getMPPCHousingHalfLength() * CLHEP::cm;
878  const CLHEP::Hep3Vector envelopeHalfSize = m_GeoPar->getScintEnvelopeHalfSize(layer, hasChimney) * CLHEP::cm +
879  CLHEP::Hep3Vector(0.0, mppcHousingHalfLength, mppcHousingHalfLength);
880  G4Box* scintEnvelopeBox =
881  new G4Box(name,
882  envelopeHalfSize.x(), envelopeHalfSize.y(), envelopeHalfSize.z()
883  );
884  G4LogicalVolume* innerEnvelopeLogical =
885  new G4LogicalVolume(scintEnvelopeBox,
886  Materials::get("G4_AIR"),
887  logicalName(scintEnvelopeBox)
888  );
889  innerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
890  double scintHalfHeight = m_GeoPar->getScintHalfHeight() * CLHEP::cm;
891  double scintHalfWidth = m_GeoPar->getScintHalfWidth() * CLHEP::cm;
892 
893  int envelopeOffsetSign = m_GeoPar->getScintEnvelopeOffsetSign(layer);
894  const Module* module = m_GeoPar->findModule(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
895  for (int scint = 1; scint <= m_GeoPar->getNPhiScints(layer); ++scint) {
896  double scintHalfLength = module->getPhiScintHalfLength(scint) * CLHEP::cm;
897  double scintPosition = module->getPhiScintPosition(scint) * CLHEP::cm;
898  G4LogicalVolume* scintLogical = getScintLogical(scintHalfHeight, scintHalfWidth, scintHalfLength,
899  mppcHousingHalfLength);
900  new G4PVPlacement(G4Translate3D(0.0, scintPosition + mppcHousingHalfLength * envelopeOffsetSign, 0.0),
901  scintLogical,
902  physicalName(scintLogical),
903  innerEnvelopeLogical,
904  false,
905  scint,
906  false
907  );
908  }
909  G4LogicalVolume* outerEnvelopeLogical =
910  new G4LogicalVolume(scintEnvelopeBox,
911  Materials::get("G4_AIR"),
912  logicalName(scintEnvelopeBox)
913  );
914  outerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
915  for (int scint = 1; scint <= m_GeoPar->getNZScints(hasChimney); ++scint) {
916  double scintHalfLength = module->getZScintHalfLength(scint) * CLHEP::cm;
917  double scintOffset = module->getZScintOffset(scint) * CLHEP::cm;
918  double scintPosition = module->getZScintPosition(scint) * CLHEP::cm;
919  G4LogicalVolume* scintLogical = getScintLogical(scintHalfHeight, scintHalfWidth, scintHalfLength,
920  mppcHousingHalfLength);
921  new G4PVPlacement(G4Translate3D(0.0, scintOffset, scintPosition - mppcHousingHalfLength) * G4RotateX3D(M_PI_2 * envelopeOffsetSign),
922  scintLogical,
923  physicalName(scintLogical),
924  outerEnvelopeLogical,
925  false,
926  scint,
927  false
928  );
929  }
930  CLHEP::Hep3Vector envelopeOffset = m_GeoPar->getScintEnvelopeOffset(layer, hasChimney) * CLHEP::cm +
931  CLHEP::Hep3Vector(0.0, -mppcHousingHalfLength, mppcHousingHalfLength);
932  new G4PVPlacement(G4Translate3D(-envelopeHalfSize.x(), envelopeOffset.y() * envelopeOffsetSign, envelopeOffset.z()),
933  innerEnvelopeLogical,
934  physicalName(innerEnvelopeLogical),
935  airLogical,
936  false,
937  BKLM_INNER,
938  false
939  );
940  new G4PVPlacement(G4Translate3D(+envelopeHalfSize.x(), envelopeOffset.y() * envelopeOffsetSign, envelopeOffset.z()),
941  outerEnvelopeLogical,
942  physicalName(outerEnvelopeLogical),
943  airLogical,
944  false,
945  BKLM_OUTER,
946  false
947  );
948 
949  // Place readout carriers and preamplifiers along module perimeter
950  double containerHalfSizeZ = m_GeoPar->getReadoutContainerHalfSize().z() * CLHEP::cm;
951  G4LogicalVolume* readoutContainerLogical = getReadoutContainerLogical();
952  for (int station = 1; station <= m_GeoPar->getNReadoutStation(); ++station) {
953  double stationPosition = m_GeoPar->getReadoutStationPosition(station) * CLHEP::cm;
954  G4Transform3D xform;
955  if (m_GeoPar->getReadoutStationIsPhi(station)) {
956  xform = G4Translate3D(0.0, stationPosition, airHalfSize.z() - containerHalfSizeZ);
957  } else {
958  xform = G4Translate3D(0.0,
959  (containerHalfSizeZ - airHalfSize.y()) * envelopeOffsetSign,
960  airHalfSize.z() + stationPosition
961  ) * G4RotateX3D(M_PI_2 * envelopeOffsetSign);
962  if (fabs(xform.getTranslation().z()) > airHalfSize.z()) continue; // don't place all z-readout stations in chimney module
963  }
964  sprintf(name, "BKLM.ReadoutContainer%dLayer%02d%sPhysical", station, layer, (hasChimney ? "Chimney" : ""));
965  new G4PVPlacement(xform,
966  readoutContainerLogical,
967  name,
968  airLogical,
969  false,
970  station,
971  false
972  );
973  }
974 
975  // Place the air container with scints, MPPCs and preamps in the interior container
976  new G4PVPlacement(G4TranslateX3D(m_GeoPar->getPolystyreneOffsetX() * CLHEP::cm),
977  airLogical,
978  physicalName(airLogical),
979  interiorLogical,
980  false,
981  1,
982  false
983  );
984 }
985 
986 G4LogicalVolume* GeoBKLMCreator::getScintLogical(double dx, double dy, double dz, double dzMPPC)
987 {
988 
989  int newLvol = 1;
990  for (std::vector<G4LogicalVolume*>::iterator iLvol = m_ScintLogicals.begin(); iLvol != m_ScintLogicals.end(); ++iLvol) {
991  G4Box* box = (G4Box*)((*iLvol)->GetSolid());
992  if ((std::fabs(box->GetXHalfLength() - dx) < 1.0E-4 * CLHEP::cm) &&
993  (std::fabs(box->GetYHalfLength() - dy) < 1.0E-4 * CLHEP::cm) &&
994  (std::fabs(box->GetZHalfLength() - dz - dzMPPC) < 1.0E-4 * CLHEP::cm)) { return *iLvol; }
995  newLvol++;
996  }
997  char name[80] = "";
998  sprintf(name, "BKLM.ScintType%dSolid", newLvol);
999  G4Box* scintBox = new G4Box(name, dx, dy, dz + dzMPPC);
1000  G4LogicalVolume* scintLogical =
1001  new G4LogicalVolume(scintBox, Materials::get("G4_POLYSTYRENE"), logicalName(scintBox));
1002  m_ScintLogicals.push_back(scintLogical);
1003  scintLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1004  sprintf(name, "BKLM.ScintType%dAirSolid", newLvol);
1005  G4Box* scintAirBox = new G4Box(name, dx, dy, dzMPPC);
1006  G4LogicalVolume* scintAirLogical =
1007  new G4LogicalVolume(scintAirBox, Materials::get("G4_AIR"), logicalName(scintAirBox));
1008  scintAirLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1009  double dxTiO2 = m_GeoPar->getScintTiO2ThicknessTop() * CLHEP::cm;
1010  double dyTiO2 = m_GeoPar->getScintTiO2ThicknessSide() * CLHEP::cm;
1011  sprintf(name, "BKLM.ScintActiveType%dSolid", newLvol);
1012  G4Box* activeBox = new G4Box(name, dx - dxTiO2, dy - dyTiO2, dz);
1013  G4LogicalVolume* activeLogical =
1014  new G4LogicalVolume(activeBox, Materials::get("G4_POLYSTYRENE"), logicalName(activeBox), 0, m_Sensitive, 0);
1015  m_VisAttributes.push_back(new G4VisAttributes(true));
1016  m_VisAttributes.back()->SetColour(1.0, 0.5, 0.0);
1017  activeLogical->SetVisAttributes(m_VisAttributes.back());
1018  sprintf(name, "BKLM.ScintBoreType%dSolid", newLvol);
1019  G4Tubs* boreTube = new G4Tubs(name, 0.0, m_GeoPar->getScintBoreRadius() * CLHEP::cm, dz, 0.0, 2.0 * M_PI);
1020  G4LogicalVolume* scintBoreLogical =
1021  new G4LogicalVolume(boreTube, Materials::get("G4_AIR"), logicalName(boreTube));
1022  scintBoreLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1023  sprintf(name, "BKLM.ScintFiberType%dSolid", newLvol);
1024  G4Tubs* fiberTube = new G4Tubs(name, 0.0, m_GeoPar->getScintFiberRadius() * CLHEP::cm, dz, 0.0, 2.0 * M_PI);
1025  G4LogicalVolume* scintFiberLogical = new G4LogicalVolume(fiberTube, Materials::get("G4_POLYSTYRENE"), logicalName(fiberTube));
1026  m_VisAttributes.push_back(new G4VisAttributes(true));
1027  m_VisAttributes.back()->SetColour(0.0, 1.0, 0.0);
1028  scintFiberLogical->SetVisAttributes(m_VisAttributes.back());
1029  new G4PVPlacement(G4TranslateZ3D(0.0),
1030  scintFiberLogical,
1031  physicalName(scintFiberLogical),
1032  scintBoreLogical,
1033  false,
1034  1,
1035  false
1036  );
1037  new G4PVPlacement(G4TranslateZ3D(0.0),
1038  scintBoreLogical,
1039  physicalName(scintBoreLogical),
1040  activeLogical,
1041  false,
1042  1,
1043  false
1044  );
1045  new G4PVPlacement(G4TranslateZ3D(-dzMPPC),
1046  activeLogical,
1047  physicalName(activeLogical),
1048  scintLogical,
1049  false,
1050  1,
1051  false
1052  );
1053  new G4PVPlacement(G4TranslateZ3D(0.0),
1054  getMPPCHousingLogical(),
1055  "BKLM.MPPCHousingPhysical",
1056  scintAirLogical,
1057  false,
1058  1,
1059  false
1060  );
1061  new G4PVPlacement(G4TranslateZ3D(dz),
1062  scintAirLogical,
1063  physicalName(scintAirLogical),
1064  scintLogical,
1065  false,
1066  1,
1067  false
1068  );
1069  return scintLogical;
1070 }
1071 
1072 G4LogicalVolume* GeoBKLMCreator::getMPPCHousingLogical()
1073 {
1074  if (m_MPPCHousingLogical == nullptr) {
1075  G4Tubs* mppcHousingSolid =
1076  new G4Tubs("BKLM.MPPCHousingSolid",
1077  0.0,
1078  m_GeoPar->getMPPCHousingRadius() * CLHEP::cm,
1079  m_GeoPar->getMPPCHousingHalfLength() * CLHEP::cm,
1080  0.0,
1081  2.0 * M_PI
1082  );
1083  m_MPPCHousingLogical =
1084  new G4LogicalVolume(mppcHousingSolid,
1085  Materials::get("G4_POLYCARBONATE"),
1086  "BKLM>MPPCHousingLogical"
1087  );
1088  m_VisAttributes.push_back(new G4VisAttributes(true));
1089  m_VisAttributes.back()->SetColour(0.5, 0.5, 0.5); // gray
1090  m_MPPCHousingLogical->SetVisAttributes(m_VisAttributes.back());
1091  G4Box* mppcBox =
1092  new G4Box("BKLM.MPPCSolid",
1093  m_GeoPar->getMPPCHalfLength() * CLHEP::cm,
1094  m_GeoPar->getMPPCHalfWidth() * CLHEP::cm,
1095  m_GeoPar->getMPPCHalfHeight() * CLHEP::cm
1096  );
1097  G4LogicalVolume* mppcLogical =
1098  new G4LogicalVolume(mppcBox,
1099  Materials::get("G4_Si"),
1100  "BKLM.MPPCLogical"
1101  );
1102  m_VisAttributes.push_back(new G4VisAttributes(true));
1103  m_VisAttributes.back()->SetColour(1.0, 1.0, 1.0); // white
1104  mppcLogical->SetVisAttributes(m_VisAttributes.back());
1105  new G4PVPlacement(G4TranslateX3D(0.0),
1106  mppcLogical,
1107  "BKLM.MPPCPhysical",
1108  m_MPPCHousingLogical,
1109  false,
1110  1,
1111  false
1112  );
1113  }
1114  return m_MPPCHousingLogical;
1115 }
1116 
1117 G4LogicalVolume* GeoBKLMCreator::getReadoutContainerLogical()
1118 {
1119  if (m_ReadoutContainerLogical == nullptr) {
1120  const CLHEP::Hep3Vector containerHalfSize = m_GeoPar->getReadoutContainerHalfSize() * CLHEP::cm;
1121  G4Box* containerBox =
1122  new G4Box("BKLM.ReadoutContainerSolid",
1123  containerHalfSize.x(), containerHalfSize.y(), containerHalfSize.z()
1124  );
1125  m_ReadoutContainerLogical =
1126  new G4LogicalVolume(containerBox,
1127  Materials::get("G4_AIR"),
1128  logicalName(containerBox)
1129  );
1130  m_ReadoutContainerLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1131 
1132  const CLHEP::Hep3Vector carrierHalfSize = m_GeoPar->getReadoutCarrierHalfSize() * CLHEP::cm;
1133  G4Box* carrierBox =
1134  new G4Box("BKLM.ReadoutCarrierSolid",
1135  carrierHalfSize.x(), carrierHalfSize.y(), carrierHalfSize.z()
1136  );
1137  G4LogicalVolume* carrierLogical =
1138  new G4LogicalVolume(carrierBox,
1139  Materials::get("NEMA_G10_Plate"), // defined in CDC
1140  logicalName(carrierBox)
1141  );
1142  m_VisAttributes.push_back(new G4VisAttributes(true));
1143  m_VisAttributes.back()->SetColour(0.0, 1.0, 0.0);
1144  carrierLogical->SetVisAttributes(m_VisAttributes.back());
1145  const CLHEP::Hep3Vector preamplifierHalfSize = m_GeoPar->getReadoutPreamplifierHalfSize() * CLHEP::cm;
1146  G4Box* preamplifierBox =
1147  new G4Box("BKLM.ReadoutPreamplifierSolid",
1148  preamplifierHalfSize.x(), preamplifierHalfSize.y(), preamplifierHalfSize.z()
1149  );
1150  G4LogicalVolume* preamplifierLogical =
1151  new G4LogicalVolume(preamplifierBox,
1152  Materials::get("NEMA_G10_Plate"), // defined in CDC
1153  logicalName(preamplifierBox)
1154  );
1155  preamplifierLogical->SetVisAttributes(m_VisAttributes.back());
1156  const CLHEP::Hep3Vector connectorsHalfSize = m_GeoPar->getReadoutConnectorsHalfSize() * CLHEP::cm;
1157  G4Box* connectorsBox =
1158  new G4Box("BKLM.ReadoutConnectorsSolid",
1159  connectorsHalfSize.x(), connectorsHalfSize.y(), connectorsHalfSize.z()
1160  );
1161  G4LogicalVolume* connectorsLogical =
1162  new G4LogicalVolume(connectorsBox,
1163  Materials::get("G4_POLYCARBONATE"),
1164  logicalName(connectorsBox)
1165  );
1166  m_VisAttributes.push_back(new G4VisAttributes(true));
1167  m_VisAttributes.back()->SetColour(0.5, 0.5, 0.5);
1168  connectorsLogical->SetVisAttributes(m_VisAttributes.back());
1169  new G4PVPlacement(G4TranslateZ3D(preamplifierHalfSize.z()),
1170  carrierLogical,
1171  physicalName(carrierLogical),
1172  m_ReadoutContainerLogical,
1173  false,
1174  1,
1175  false
1176  );
1177  new G4PVPlacement(G4Translate3D(0.0, m_GeoPar->getReadoutConnectorsPosition(), -carrierHalfSize.z()),
1178  connectorsLogical,
1179  physicalName(connectorsLogical),
1180  m_ReadoutContainerLogical,
1181  false,
1182  1,
1183  false
1184  );
1185  for (int preamp = 1; preamp <= m_GeoPar->getNReadoutPreamplifierPosition(); ++preamp) {
1186  new G4PVPlacement(G4Translate3D(0.0,
1187  m_GeoPar->getReadoutPreamplifierPosition(preamp) * CLHEP::cm,
1188  -carrierHalfSize.z()),
1189  preamplifierLogical,
1190  physicalName(preamplifierLogical),
1191  m_ReadoutContainerLogical,
1192  false,
1193  1,
1194  false
1195  );
1196  }
1197  }
1198  return m_ReadoutContainerLogical;
1199 }
1200 
1201 G4Tubs* GeoBKLMCreator::getSolenoidTube(void)
1202 {
1203 
1204  if (m_SolenoidTube == nullptr) {
1205  m_SolenoidTube =
1206  new G4Tubs("BKLM.SolenoidTube",
1207  0.0,
1208  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
1209  2.0 * m_GeoPar->getHalfLength() * CLHEP::cm,
1210  0.0,
1211  2.0 * M_PI
1212  );
1213  }
1214  return m_SolenoidTube;
1215 }
1216 
1217 G4String GeoBKLMCreator::logicalName(G4VSolid* solid)
1218 {
1219  G4String* name = new G4String(solid->GetName().substr(0, solid->GetName().size() - 5) + "Logical");
1220  m_Names.push_back(name);
1221  return *name;
1222 }
1223 
1224 G4String GeoBKLMCreator::physicalName(G4LogicalVolume* lvol)
1225 {
1226  G4String* name = new G4String(lvol->GetName().substr(0, lvol->GetName().size() - 7) + "Physical");
1227  m_Names.push_back(name);
1228  return *name;
1229 }
Belle2::geometry
Common code concerning the geometry representation of the detector.
Definition: CreatorBase.h:28
Belle2::bklm::Module
Define the geometry of a BKLM module Each sector [octant] contains Modules.
Definition: Module.h:86
Belle2::BKLMGeometryPar
The Class for BKLM geometry.
Definition: BKLMGeometryPar.h:42
Belle2::bklm::SensitiveDetector
Class for the BKLM Sensitive Detector Each qualified simulation step is saved into a StoreArray of BK...
Definition: SensitiveDetector.h:43
Belle2::geometry::GeometryTypes
GeometryTypes
Flag indiciating the type of geometry to be used.
Definition: GeometryManager.h:39