Belle II Software  release-08-01-10
GeoBKLMCreator.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 /* Own header. */
10 #include <klm/bklm/geometry/GeoBKLMCreator.h>
11 
12 /* KLM headers. */
13 #include <klm/dataobjects/bklm/BKLMElementNumbers.h>
14 #include <klm/dataobjects/KLMElementNumbers.h>
15 #include <klm/dbobjects/bklm/BKLMGeometryPar.h>
16 #include <klm/simulation/SensitiveDetector.h>
17 
18 /* Basf2 headers. */
19 #include <geometry/Materials.h>
20 
21 /* Geant 4 headers. */
22 #include <G4Box.hh>
23 #include <G4LogicalVolume.hh>
24 #include <G4Polyhedra.hh>
25 #include <G4PVPlacement.hh>
26 #include <G4Region.hh>
27 #include <G4String.hh>
28 #include <G4SubtractionSolid.hh>
29 #include <G4Transform3D.hh>
30 #include <G4Tubs.hh>
31 #include <G4UnionSolid.hh>
32 #include <G4VisAttributes.hh>
33 
34 using namespace Belle2::bklm;
35 using namespace Belle2::geometry;
36 
37 //-----------------------------------------------------------------
38 // Implementation
39 //-----------------------------------------------------------------
40 
42 {
43  m_Sensitive = dynamic_cast<G4VSensitiveDetector*>(new KLM::SensitiveDetector(G4String("BKLM"), KLMElementNumbers::c_BKLM));
44  m_GeoPar = nullptr;
45  m_SectorDphi = 0.0;
46  m_SectorDz = 0.0;
47  m_RibShift = 0.0;
48  m_CapSolid = nullptr;
49  m_CapLogical[0] = m_CapLogical[1] = nullptr;
50  m_InnerIronSolid = nullptr;
51  m_InnerIronLogical[0] = m_InnerIronLogical[1] = m_InnerIronLogical[2] = m_InnerIronLogical[3] = nullptr;
52  m_InnerAirSolid = nullptr;
53  m_InnerAirLogical[0] = m_InnerAirLogical[1] = m_InnerAirLogical[2] = m_InnerAirLogical[3] = nullptr;
54  m_SupportLogical[0] = m_SupportLogical[1] = nullptr;
55  m_BracketLogical = nullptr;
56  for (int j = 0; j < BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
57  m_LayerIronSolid[j] = nullptr;
58  }
59  for (int j = 0; j < 2 * BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
60  m_LayerModuleLogical[j] = nullptr;
61  m_LayerGapSolid[j] = nullptr;
62  }
63  for (int j = 0; j < 12 * BKLMElementNumbers::getMaximalLayerNumber(); ++j) {
64  m_LayerIronLogical[j] = nullptr;
65  m_LayerGapLogical[j] = nullptr;
66  }
67  m_SectorTube = nullptr;
68  for (int sector = 0; sector < BKLMElementNumbers::getMaximalSectorNumber(); ++sector) {
69  m_SectorLogical[0][sector] = nullptr;
70  m_SectorLogical[1][sector] = nullptr;
71  }
72  m_MPPCHousingLogical = nullptr;
73  m_ReadoutContainerLogical = nullptr;
74  m_SolenoidTube = nullptr;
75  m_ScintLogicals.clear();
76  m_VisAttributes.clear();
77  m_VisAttributes.push_back(new G4VisAttributes(false)); // for "invisible"
78  m_Names.clear();
79 }
80 
82 {
83  delete m_Sensitive;
84  m_ScintLogicals.clear();
85  for (G4VisAttributes* visAttr : m_VisAttributes) delete visAttr;
86  m_VisAttributes.clear();
87  for (G4String* name : m_Names) delete name;
88  m_Names.clear();
89 }
90 
91 //-----------------------------------------------------------------
92 // Build and place the BKLM
93 //-----------------------------------------------------------------
94 
95 void GeoBKLMCreator::createGeometry(const BKLMGeometryPar& parameters, G4LogicalVolume& motherLogical, GeometryTypes)
96 {
97 
98  m_GeoPar = GeometryPar::instance(parameters);
99  m_SectorDphi = 2.0 * M_PI / m_GeoPar->getNSector();
100  m_SectorDz = 0.5 * m_GeoPar->getHalfLength() * CLHEP::cm;
101  m_RibShift = 0.5 * m_GeoPar->getRibThickness() * CLHEP::cm / sin(0.5 * m_SectorDphi);
102 
103  // Place BKLM envelope in mother volume
104  G4Tubs* envelopeSolid =
105  new G4Tubs("BKLM.EnvelopeSolid",
106  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
107  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
108  2.0 * m_SectorDz,
109  0.0,
110  2.0 * M_PI
111  );
112  G4LogicalVolume* envelopeLogical =
113  new G4LogicalVolume(envelopeSolid,
114  Materials::get("G4_AIR"),
115  "BKLM.EnvelopeLogical"
116  );
117  envelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
118  putEndsInEnvelope(envelopeLogical);
119  /* Set up region for production cuts. */
120  G4Region* aRegion = new G4Region("BKLMEnvelope");
121  envelopeLogical->SetRegion(aRegion);
122  aRegion->AddRootLogicalVolume(envelopeLogical);
123 
124  new G4PVPlacement(G4TranslateZ3D(m_GeoPar->getOffsetZ() * CLHEP::cm) * G4RotateZ3D(m_GeoPar->getRotation() * CLHEP::rad),
125  envelopeLogical,
126  "BKLM.EnvelopePhysical",
127  &motherLogical,
128  false,
129  1,
130  false
131  );
132 
133 }
134 
135 void GeoBKLMCreator::putEndsInEnvelope(G4LogicalVolume* envelopeLogical)
136 {
137  G4Tubs* endSolid =
138  new G4Tubs("BKLM.EndSolid",
139  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
140  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
141  m_SectorDz,
142  0.0,
143  2.0 * M_PI
144  );
145  G4LogicalVolume* frontLogical =
146  new G4LogicalVolume(endSolid,
147  Materials::get("G4_AIR"),
148  "BKLM.F_Logical"
149  );
150  frontLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
151  putSectorsInEnd(frontLogical, BKLMElementNumbers::c_ForwardSection);
152  new G4PVPlacement(G4TranslateZ3D(m_SectorDz),
153  frontLogical,
154  "BKLM.F_Physical",
155  envelopeLogical,
156  false,
158  false
159  );
160  G4LogicalVolume* backLogical =
161  new G4LogicalVolume(endSolid,
162  Materials::get("G4_AIR"),
163  "BKLM.B_Logical"
164  );
165  backLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
166  putSectorsInEnd(backLogical, BKLMElementNumbers::c_BackwardSection);
167  new G4PVPlacement(G4TranslateZ3D(-m_SectorDz) * G4RotateY3D(M_PI),
168  backLogical,
169  "BKLM.B_Physical",
170  envelopeLogical,
171  false,
173  false
174  );
175 
176 }
177 
178 void GeoBKLMCreator::putSectorsInEnd(G4LogicalVolume* endLogical, int section)
179 {
180  if (m_SectorTube == nullptr) {
181  m_SectorTube =
182  new G4Tubs("BKLM.SectorSolid",
183  m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
184  m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
185  m_SectorDz,
186  -0.5 * m_SectorDphi,
187  m_SectorDphi
188  );
189  }
190  char name[80] = "";
191  for (int s = 0; s < m_GeoPar->getNSector(); ++s) {
192  int sector = (section == BKLMElementNumbers::c_ForwardSection ? s : ((12 - s) % 8)) + 1;
193  bool hasChimney = (section == BKLMElementNumbers::c_BackwardSection) && (sector == BKLMElementNumbers::c_ChimneySector);
194  bool hasInnerSupport = (sector <= m_GeoPar->getNSector() / 2 + 1);
195  sprintf(name, "BKLM.%sSector%dLogical", (section == BKLMElementNumbers::c_ForwardSection ? "Forward" : "Backward"), sector);
196  m_SectorLogical[section][sector - 1] =
197  new G4LogicalVolume(m_SectorTube,
198  Materials::get("G4_AIR"),
199  name
200  );
201  m_SectorLogical[section][sector - 1]->SetVisAttributes(m_VisAttributes.front()); // invisible
202  putCapInSector(m_SectorLogical[section][sector - 1], hasChimney);
203  putInnerRegionInSector(m_SectorLogical[section][sector - 1], hasInnerSupport, hasChimney);
204  putLayersInSector(m_SectorLogical[section][sector - 1], section, sector, hasChimney);
205  new G4PVPlacement(G4RotateZ3D(m_SectorDphi * s),
206  m_SectorLogical[section][sector - 1],
207  physicalName(m_SectorLogical[section][sector - 1]),
208  endLogical,
209  false,
210  sector,
211  false
212  );
213  }
214 }
215 
216 void GeoBKLMCreator::putCapInSector(G4LogicalVolume* sectorLogical, bool hasChimney)
217 {
218 
219  // Fill cap with iron and (aluminum) cables
220  const CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(0, false) * CLHEP::cm;
221  const double dyBrace = (hasChimney ? m_GeoPar->getBraceWidthChimney() : m_GeoPar->getBraceWidth()) * CLHEP::cm;
222  const double dy = 0.25 * (m_GeoPar->getCablesWidth() * CLHEP::cm - dyBrace);
223  const double dz = m_SectorDz - gapHalfSize.z();
224  const double ri = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm + 2.0 * gapHalfSize.x();
225  const double ro = m_GeoPar->getOuterRadius() * CLHEP::cm;
226  if (m_CapSolid == nullptr) {
227  const double z[2] = { -dz, dz};
228  const double rInner[2] = {ri, ri};
229  const double rOuter[2] = {ro, ro};
230  m_CapSolid =
231  new G4Polyhedra("BKLM.CapSolid",
232  -0.5 * m_SectorDphi,
233  m_SectorDphi,
234  1,
235  2, z, rInner, rOuter
236  );
237  }
238  int newLvol = (hasChimney ? 1 : 0);
239  G4String name = (hasChimney ? "BKLM.ChimneyCapLogical" : "BKLM.CapLogical");
240  if (m_CapLogical[newLvol] == nullptr) {
241  m_CapLogical[newLvol] =
242  new G4LogicalVolume(m_CapSolid,
243  Materials::get("G4_Fe"),
244  name
245  );
246  m_CapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
247  name = (hasChimney ? "BKLM.ChimneyCablesSolid" : "BKLM.CablesSolid");
248  G4Box* cablesSolid =
249  new G4Box(name, 0.5 * (ro - ri), dy, dz);
250  G4LogicalVolume* cablesLogical =
251  new G4LogicalVolume(cablesSolid,
252  Materials::get("G4_Al"),
253  logicalName(cablesSolid)
254  );
255  cablesLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
256  new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), -(0.5 * dyBrace + dy), 0.0),
257  cablesLogical,
258  physicalName(cablesLogical).append(G4String("_L")),
259  m_CapLogical[newLvol],
260  false,
261  1,
262  false
263  );
264  new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), +(0.5 * dyBrace + dy), 0.0),
265  cablesLogical,
266  physicalName(cablesLogical).append(G4String("_R")),
267  m_CapLogical[newLvol],
268  false,
269  2,
270  false
271  );
272  }
273  new G4PVPlacement(G4TranslateZ3D(m_SectorDz - dz),
274  m_CapLogical[newLvol],
275  physicalName(m_CapLogical[newLvol]),
276  sectorLogical,
277  false,
278  1,
279  false
280  );
281 }
282 
283 void GeoBKLMCreator::putInnerRegionInSector(G4LogicalVolume* sectorLogical, bool hasInnerSupport, bool hasChimney)
284 {
285 
286  // Fill inner region with iron
287  if (m_InnerIronSolid == nullptr) {
288  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm;
289  const double z[2] = { -m_SectorDz, +m_SectorDz};
290  const double rInner[2] = {0.0, 0.0};
291  const double rOuter[2] = {r, r};
292  G4Polyhedra* innerIronPolygon =
293  new G4Polyhedra("BKLM.InnerIronPolygon",
294  -0.5 * m_SectorDphi,
295  m_SectorDphi,
296  1,
297  2, z, rInner, rOuter
298  );
299  m_InnerIronSolid =
300  new G4SubtractionSolid("BKLM.InnerIronSolid",
301  innerIronPolygon,
302  getSolenoidTube()
303  );
304  }
305  int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
306  if (m_InnerIronLogical[newLvol] == nullptr) {
307  m_InnerIronLogical[newLvol] =
308  new G4LogicalVolume(m_InnerIronSolid,
309  Materials::get("G4_Fe"),
310  (hasChimney ? "BKLM.InnerIronChimneyLogical" : "BKLM.InnerIronLogical")
311  );
312  m_InnerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
313  putVoidInInnerRegion(m_InnerIronLogical[newLvol], hasInnerSupport, hasChimney);
314  }
315  new G4PVPlacement(G4TranslateZ3D(0.0),
316  m_InnerIronLogical[newLvol],
317  physicalName(m_InnerIronLogical[newLvol]),
318  sectorLogical,
319  false,
320  1,
321  false
322  );
323 }
324 
325 void GeoBKLMCreator::putVoidInInnerRegion(G4LogicalVolume* innerIronLogical, bool hasInnerSupport, bool hasChimney)
326 {
327 
328  // Carve out an air void from the inner-region iron, leaving only the ribs
329  // at the azimuthal edges
330  if (m_InnerAirSolid == nullptr) {
331  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - m_RibShift;
332  const double z[2] = { -m_SectorDz, +m_SectorDz};
333  const double rInner[2] = {0.0, 0.0};
334  const double rOuter[2] = {r, r};
335  G4Polyhedra* innerAirPolygon =
336  new G4Polyhedra("BKLM.InnerAirPolygon",
337  -0.5 * m_SectorDphi,
338  m_SectorDphi,
339  1,
340  2, z, rInner, rOuter
341  );
342  m_InnerAirSolid =
343  new G4SubtractionSolid("BKLM.InnerAirSolid",
344  innerAirPolygon,
345  getSolenoidTube(),
346  G4TranslateX3D(-m_RibShift)
347  );
348  }
349  int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
350  if (m_InnerAirLogical[newLvol] == nullptr) {
351  m_InnerAirLogical[newLvol] =
352  new G4LogicalVolume(m_InnerAirSolid,
353  Materials::get("G4_AIR"),
354  (hasChimney ? "BKLM.InnerAirChimneyLogical" : "BKLM.InnerAirLogical")
355  );
356  m_InnerAirLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
357  if (hasInnerSupport) {
358  putLayer1SupportInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
359  putLayer1BracketsInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
360  }
361  }
362  new G4PVPlacement(G4TranslateX3D(m_RibShift),
363  m_InnerAirLogical[newLvol],
364  physicalName(m_InnerAirLogical[newLvol]),
365  innerIronLogical,
366  false,
367  1,
368  false
369  );
370 }
371 
372 void GeoBKLMCreator::putLayer1SupportInInnerVoid(G4LogicalVolume* innerAirLogical, bool hasChimney)
373 {
374 
375  int newLvol = (hasChimney ? 1 : 0);
376  const CLHEP::Hep3Vector size = m_GeoPar->getSupportPlateHalfSize(hasChimney) * CLHEP::cm;
377  if (m_SupportLogical[newLvol] == nullptr) {
378  G4Box* supportBox =
379  new G4Box((hasChimney ? "BKLM.ChimneySupportSolid" : "BKLM.SupportSolid"),
380  size.x(),
381  size.y(),
382  size.z()
383  );
384  m_SupportLogical[newLvol] =
385  new G4LogicalVolume(supportBox,
386  Materials::get("G4_Al"),
387  logicalName(supportBox)
388  );
389  m_SupportLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
390  }
391  double dx = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - size.x() - m_RibShift;
392  double dz = size.z() - m_SectorDz;
393  new G4PVPlacement(G4Translate3D(dx, 0.0, dz),
394  m_SupportLogical[newLvol],
395  physicalName(m_SupportLogical[newLvol]),
396  innerAirLogical,
397  false,
398  1,
399  false
400  );
401 }
402 
403 void GeoBKLMCreator::putLayer1BracketsInInnerVoid(G4LogicalVolume* innerAirLogical, bool hasChimney)
404 {
405 
406  if (m_BracketLogical == nullptr) {
407  const CLHEP::Hep3Vector size = m_GeoPar->getSupportPlateHalfSize(hasChimney) * CLHEP::cm;
408  const double dz = 0.5 * m_GeoPar->getBracketLength() * CLHEP::cm;
409  const double r = m_GeoPar->getLayerInnerRadius(1) * CLHEP::cm - m_RibShift - 2.0 * size.x();
410  const double bracketShift = m_GeoPar->getBracketRibThickness() * CLHEP::cm / sin(0.5 * m_SectorDphi);
411  const double z[2] = { -dz, +dz};
412  const double rInner[2] = {0.0, 0.0};
413  const double rOuter[2] = {r, r};
414  const double r1 = m_GeoPar->getBracketInnerRadius() * CLHEP::cm - m_RibShift;
415  const double z1[2] = { -(dz + 0.5 * CLHEP::cm), dz + 0.5 * CLHEP::cm};
416  const double rOuter1[2] = {r1, r1};
417  const double z2[2] = { -(dz + 1.0 * CLHEP::cm), dz + 1.0 * CLHEP::cm};
418  const double dzBracket = m_GeoPar->getBracketThickness() * CLHEP::cm;
419  const double drBracket = dzBracket + bracketShift;
420  const double rOuter2[2] = {r - drBracket, r - drBracket};
421  const double z3[2] = { -dzBracket, dzBracket};
422  const double cutoutDphi = m_GeoPar->getBracketCutoutDphi() * CLHEP::rad;
423  G4Polyhedra* bracketPolygon =
424  new G4Polyhedra("BKLM.BracketPolygon",
425  -0.5 * m_SectorDphi,
426  m_SectorDphi,
427  1,
428  2, z, rInner, rOuter
429  );
430  G4Polyhedra* bracketCutout1 =
431  new G4Polyhedra("BKLM.BracketCutout1",
432  -0.5 * cutoutDphi,
433  cutoutDphi,
434  2, // two sides
435  2, z1, rInner, rOuter1
436  );
437  G4Polyhedra* bracketCutout2 =
438  new G4Polyhedra("BKLM.BracketCutout2",
439  -0.5 * m_SectorDphi,
440  m_SectorDphi,
441  1,
442  2, z2, rInner, rOuter2
443  );
444  G4Polyhedra* bracketCutout3 =
445  new G4Polyhedra("BKLM.BracketCutout3",
446  -0.5 * m_SectorDphi,
447  m_SectorDphi,
448  1,
449  2, z3, rInner, rOuter2
450  );
451  G4Box* bracketCutout4 =
452  new G4Box("BKLM.BracketCutout4",
453  rOuter[1] + 1.0 * CLHEP::cm,
454  rOuter[1] * tan(0.5 * m_SectorDphi) - m_GeoPar->getBracketWidth() * CLHEP::cm,
455  z[1] + 1.5 * CLHEP::cm
456  );
457  G4VSolid* polygon1 =
458  new G4SubtractionSolid("BKLM.BracketPolygon1",
459  bracketPolygon,
460  bracketCutout4
461  );
462  G4VSolid* polygon2 =
463  new G4SubtractionSolid("BKLM.BracketPolygon2",
464  bracketCutout2,
465  bracketCutout3
466  );
467  G4VSolid* polygon3 =
468  new G4SubtractionSolid("BKLM.BracketPolygon3",
469  polygon1,
470  polygon2,
471  G4TranslateX3D(bracketShift)
472  );
473  G4VSolid* bracketSolid =
474  new G4SubtractionSolid("BKLM.BracketSolid",
475  polygon3,
476  bracketCutout1
477  );
478  m_BracketLogical =
479  new G4LogicalVolume(bracketSolid,
480  Materials::get("G4_Al"),
481  "BKLM.BracketLogical"
482  );
483  m_VisAttributes.push_back(new G4VisAttributes(true));
484  m_VisAttributes.back()->SetColour(0.6, 0.6, 0.6);
485  m_BracketLogical->SetVisAttributes(m_VisAttributes.back());
486  }
487  char name[80] = "";
488  for (int bracket = 0; bracket < (hasChimney ? 2 : 3); ++bracket) {
489  sprintf(name, "BKLM.Bracket%d%sPhysical", bracket, (hasChimney ? "Chimney" : ""));
490  new G4PVPlacement(G4TranslateZ3D(m_GeoPar->getBracketZPosition(bracket, hasChimney) * CLHEP::cm),
491  m_BracketLogical,
492  name,
493  innerAirLogical,
494  false,
495  bracket,
496  false
497  );
498  }
499 }
500 
501 void GeoBKLMCreator::putLayersInSector(G4LogicalVolume* sectorLogical, int section, int sector, bool hasChimney)
502 {
503 
504  const double dz = 0.5 * m_GeoPar->getGapLength() * CLHEP::cm;
505  const double z[2] = { -dz, +dz };
506  char name[80] = "";
507  for (int layer = 1; layer <= m_GeoPar->getNLayer(); ++layer) {
508  // Fill layer with iron
509  if (m_LayerIronSolid[layer - 1] == nullptr) {
510  const double ri = m_GeoPar->getLayerInnerRadius(layer) * CLHEP::cm;
511  const double ro = m_GeoPar->getLayerOuterRadius(layer) * CLHEP::cm;
512  const double rInner[2] = {ri, ri};
513  const double rOuter[2] = {ro, ro};
514  sprintf(name, "BKLM.Layer%02dIronSolid", layer);
515  m_LayerIronSolid[layer - 1] =
516  new G4Polyhedra(name,
517  -0.5 * m_SectorDphi,
518  m_SectorDphi,
519  1,
520  2, z, rInner, rOuter
521  );
522  }
523  const Module* module = m_GeoPar->findModule(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
524  bool isFlipped = module->isFlipped();
525  int newLvol = BKLMElementNumbers::getMaximalLayerNumber() * ((isFlipped ? 2 : 0) +
526  (hasChimney ? 1 : 0)) + (layer - 1);
527  int s1 = sector - 1;
528  int s2 = m_GeoPar->getNSector() / 2;
529  if (s1 % s2 == 0) {
530  } else if (s1 > s2) {
532  } else {
534  }
535  if (m_LayerIronLogical[newLvol] == nullptr) {
536  sprintf(name, "BKLM.Layer%02d%s%sIronLogical", layer, (isFlipped ? "Flipped" : ""), (hasChimney ? "Chimney" : ""));
537  m_LayerIronLogical[newLvol] =
538  new G4LogicalVolume(m_LayerIronSolid[layer - 1],
539  Materials::get("G4_Fe"),
540  name
541  );
542  m_LayerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
543  putModuleInLayer(m_LayerIronLogical[newLvol], section, sector, layer, hasChimney, isFlipped, newLvol);
544  if (hasChimney) {
545  putChimneyInLayer(m_LayerIronLogical[newLvol], layer);
546  }
547  }
548  new G4PVPlacement(G4TranslateZ3D(dz - m_SectorDz),
549  m_LayerIronLogical[newLvol],
550  physicalName(m_LayerIronLogical[newLvol]),
551  sectorLogical,
552  false,
553  layer,
554  false
555  );
556 
557  }
558 }
559 
560 void GeoBKLMCreator::putChimneyInLayer(G4LogicalVolume* layerIronLogical, int layer)
561 {
562  CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(layer, true) * CLHEP::cm;
563  CLHEP::Hep3Vector chimneyHalfSize = m_GeoPar->getChimneyHalfSize(layer) * CLHEP::cm;
564  CLHEP::Hep3Vector chimneyPosition = m_GeoPar->getChimneyPosition(layer) * CLHEP::cm;
565  gapHalfSize.setY(0.5 * (gapHalfSize.y() - chimneyHalfSize.y()));
566  gapHalfSize.setZ(chimneyHalfSize.z() + 0.5 * m_GeoPar->getChimneyCoverThickness() * CLHEP::cm);
567  double dx = m_GeoPar->getGapMiddleRadius(layer) * CLHEP::cm;
568  double dy = gapHalfSize.y() + chimneyHalfSize.y();
569  double dz = 0.5 * m_GeoPar->getGapLength() * CLHEP::cm - gapHalfSize.z();
570  char name[80] = "";
571  // Fill the two chimney gaps with air
572  sprintf(name, "BKLM.Layer%02dGapChimneyBox", layer);
573  G4Box* gapBox =
574  new G4Box(name,
575  gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
576  );
577  sprintf(name, "BKLM.Layer%02dGapChimneyLogical", layer);
578  G4LogicalVolume* gapLogical =
579  new G4LogicalVolume(gapBox,
580  Materials::get("G4_AIR"),
581  name
582  );
583  gapLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
584  sprintf(name, "BKLM.Layer%02dLeftGapChimneyPhysical", layer);
585  new G4PVPlacement(G4Translate3D(dx, -dy, dz),
586  gapLogical,
587  name,
588  layerIronLogical,
589  false,
590  0,
591  false
592  );
593  sprintf(name, "BKLM.Layer%02dRightGapChimneyPhysical", layer);
594  new G4PVPlacement(G4Translate3D(dx, +dy, dz),
595  gapLogical,
596  name,
597  layerIronLogical,
598  false,
599  1,
600  false
601  );
602  // Fill chimney with air
603  sprintf(name, "BKLM.Layer%02dChimneyBox", layer);
604  G4Box* chimneyBox =
605  new G4Box(name,
606  chimneyHalfSize.x(), chimneyHalfSize.y(), chimneyHalfSize.z()
607  );
608  sprintf(name, "BKLM.Layer%02dChimneyLogical", layer);
609  G4LogicalVolume* chimneyLogical =
610  new G4LogicalVolume(chimneyBox,
611  Materials::get("G4_AIR"),
612  name
613  );
614  chimneyLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
615  // Place coaxial tubes in chimney
616  G4Tubs* housingTube =
617  new G4Tubs("BKLM.ChimneyHousingTube",
618  m_GeoPar->getChimneyHousingInnerRadius() * CLHEP::cm,
619  m_GeoPar->getChimneyHousingOuterRadius() * CLHEP::cm,
620  chimneyHalfSize.x(),
621  0.0,
622  2.0 * M_PI
623  );
624  G4LogicalVolume* housingLogical =
625  new G4LogicalVolume(housingTube,
626  Materials::get("G4_Fe"),
627  "BKLM.ChimneyHousingLogical"
628  );
629  m_VisAttributes.push_back(new G4VisAttributes(true));
630  m_VisAttributes.back()->SetColour(0.4, 0.4, 0.4);
631  housingLogical->SetVisAttributes(m_VisAttributes.back());
632  new G4PVPlacement(G4RotateY3D(M_PI_2),
633  housingLogical,
634  "BKLM.ChimneyHousingPhysical",
635  chimneyLogical,
636  false,
637  0,
638  false
639  );
640  G4Tubs* shieldTube =
641  new G4Tubs("BKLM.ChimneyShieldTube",
642  m_GeoPar->getChimneyShieldInnerRadius() * CLHEP::cm,
643  m_GeoPar->getChimneyShieldOuterRadius() * CLHEP::cm,
644  chimneyHalfSize.x(),
645  0.0,
646  2.0 * M_PI
647  );
648  G4LogicalVolume* shieldLogical =
649  new G4LogicalVolume(shieldTube,
650  Materials::get("G4_Fe"),
651  "BKLM.ChimneyShieldLogical"
652  );
653  shieldLogical->SetVisAttributes(m_VisAttributes.back());
654  new G4PVPlacement(G4RotateY3D(M_PI_2),
655  shieldLogical,
656  "BKLM.ChimneyShieldPhysical",
657  chimneyLogical,
658  false,
659  0,
660  false
661  );
662  G4Tubs* pipeTube =
663  new G4Tubs("BKLM.ChimneyPipeTube",
664  m_GeoPar->getChimneyPipeInnerRadius() * CLHEP::cm,
665  m_GeoPar->getChimneyPipeOuterRadius() * CLHEP::cm,
666  chimneyHalfSize.x(),
667  0.0,
668  2.0 * M_PI
669  );
670  G4LogicalVolume* pipeLogical =
671  new G4LogicalVolume(pipeTube,
672  Materials::get("G4_Fe"),
673  "BKLM.ChimneyPipeLogical"
674  );
675  m_VisAttributes.push_back(new G4VisAttributes(true));
676  m_VisAttributes.back()->SetColour(0.6, 0.6, 0.6);
677  pipeLogical->SetVisAttributes(m_VisAttributes.back());
678  new G4PVPlacement(G4RotateY3D(M_PI_2),
679  pipeLogical,
680  "BKLM.ChimneyPipePhysical",
681  chimneyLogical,
682  false,
683  0,
684  false
685  );
686  sprintf(name, "BKLM.Layer%02dChimneyPhysical", layer);
687  new G4PVPlacement(G4Translate3D(chimneyPosition),
688  chimneyLogical,
689  name,
690  layerIronLogical,
691  false,
692  0,
693  false
694  );
695 }
696 
697 void GeoBKLMCreator::putModuleInLayer(G4LogicalVolume* layerIronLogical, int section, int sector, int layer, bool hasChimney,
698  bool isFlipped, int newLvol)
699 {
700  const CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(layer, hasChimney) * CLHEP::cm;
701  const CLHEP::Hep3Vector moduleHalfSize = m_GeoPar->getModuleHalfSize(layer, hasChimney) * CLHEP::cm;
702  char name[80] = "";
703  // Fill gap with air
704  int modLvol = (hasChimney ? BKLMElementNumbers::getMaximalLayerNumber() : 0) + (layer - 1);
705  if (m_LayerModuleLogical[modLvol] == nullptr) {
706  // Module is aluminum (but interior will be filled)
707  sprintf(name, "BKLM.Layer%02d%sModuleSolid", layer, (hasChimney ? "Chimney" : ""));
708  G4Box* moduleBox =
709  new G4Box(name,
710  moduleHalfSize.x(), moduleHalfSize.y(), moduleHalfSize.z()
711  );
712  m_LayerModuleLogical[modLvol] =
713  new G4LogicalVolume(moduleBox,
714  Materials::get("G4_Al"),
715  logicalName(moduleBox)
716  );
717  m_LayerModuleLogical[modLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
718  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid1", layer, (hasChimney ? "Chimney" : ""));
719  const CLHEP::Hep3Vector interiorHalfSize1 = m_GeoPar->getModuleInteriorHalfSize1(layer, hasChimney) * CLHEP::cm;
720  G4Box* interiorBox1 =
721  new G4Box(name,
722  interiorHalfSize1.x(), interiorHalfSize1.y(), interiorHalfSize1.z()
723  );
724  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid2", layer, (hasChimney ? "Chimney" : ""));
725  const CLHEP::Hep3Vector interiorHalfSize2 = m_GeoPar->getModuleInteriorHalfSize2(layer, hasChimney) * CLHEP::cm;
726  G4Box* interiorBox2 =
727  new G4Box(name,
728  interiorHalfSize2.x(), interiorHalfSize2.y(), interiorHalfSize2.z()
729  );
730  sprintf(name, "BKLM.Layer%02d%sModuleInteriorSolid", layer, (hasChimney ? "Chimney" : ""));
731  G4UnionSolid* interiorUnion =
732  new G4UnionSolid(name, interiorBox1, interiorBox2);
733  G4LogicalVolume* interiorLogical =
734  new G4LogicalVolume(interiorUnion,
735  Materials::get((m_GeoPar->hasRPCs(layer) ? "RPCReadout" : "G4_POLYSTYRENE")),
736  logicalName(interiorUnion)
737  );
738  interiorLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
739  if (m_GeoPar->hasRPCs(layer)) {
740  putRPCsInInterior(interiorLogical, layer, hasChimney);
741  } else {
742  putScintsInInterior(interiorLogical, section, sector, layer, hasChimney);
743  }
744  new G4PVPlacement(G4TranslateZ3D(0.0),
745  interiorLogical,
746  physicalName(interiorLogical),
747  m_LayerModuleLogical[modLvol],
748  false,
749  0,
750  false
751  );
752  sprintf(name, "BKLM.Layer%02d%sGapSolid", layer, (hasChimney ? "Chimney" : ""));
753  m_LayerGapSolid[modLvol] =
754  new G4Box(name,
755  gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
756  );
757  }
758  if (m_LayerGapLogical[newLvol] == nullptr) {
759  sprintf(name, "BKLM.Layer%02d%s%sGapLogical", layer, (isFlipped ? "Flipped" : ""), (hasChimney ? "Chimney" : ""));
760  m_LayerGapLogical[newLvol] =
761  new G4LogicalVolume(m_LayerGapSolid[modLvol],
762  Materials::get("G4_AIR"),
763  name
764  );
765  m_LayerGapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front()); // invisible
766  }
767  double dx = (m_GeoPar->getModuleMiddleRadius(layer) - m_GeoPar->getGapMiddleRadius(layer)) * CLHEP::cm;
768  // Module is closer to IP within gap if in the upper octants, farther from IP within gap if in the lower octants,
769  // or in the middle of the gap if in the side octants.
770  int s1 = sector - 1;
771  int s2 = m_GeoPar->getNSector() / 2;
772  if (s1 % s2 == 0) {
773  dx = 0.0;
774  } else if (s1 > s2) {
775  dx = -dx;
776  }
777  double dz = moduleHalfSize.z() - gapHalfSize.z();
778  G4Transform3D displacedGeo = m_GeoPar->getModuleDisplacedGeo(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
779  new G4PVPlacement(G4Translate3D(dx, 0.0, dz) * G4RotateZ3D(isFlipped ? M_PI : 0.0) * displacedGeo,
780  m_LayerModuleLogical[modLvol],
781  physicalName(m_LayerModuleLogical[modLvol]),
782  m_LayerGapLogical[newLvol],
783  false,
784  0,
785  false
786  );
787  dz = gapHalfSize.z() - 0.5 * m_GeoPar->getGapLength() * CLHEP::cm;
788  new G4PVPlacement(G4Translate3D(m_GeoPar->getGapMiddleRadius(layer) * CLHEP::cm, 0.0, dz),
789  m_LayerGapLogical[newLvol],
790  physicalName(m_LayerGapLogical[newLvol]),
791  layerIronLogical,
792  false,
793  layer,
794  false
795  );
796 }
797 
798 void GeoBKLMCreator::putRPCsInInterior(G4LogicalVolume* interiorLogical, int layer, bool hasChimney)
799 {
800  char name[80] = "";
801  // Place electrode inside the module's interior
802  sprintf(name, "BKLM.Layer%02d%sElectrodeSolid", layer, (hasChimney ? "Chimney" : ""));
803  const CLHEP::Hep3Vector electrodeHalfSize = m_GeoPar->getElectrodeHalfSize(layer, hasChimney) * CLHEP::cm;
804  G4Box* electrodeBox =
805  new G4Box(name,
806  electrodeHalfSize.x(), electrodeHalfSize.y(), electrodeHalfSize.z()
807  );
808  G4LogicalVolume* electrodeLogical =
809  new G4LogicalVolume(electrodeBox,
810  Materials::get("G4_GLASS_PLATE"),
811  logicalName(electrodeBox)
812  );
813  electrodeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
814  // Place two gas volumes inside electrodes
815  sprintf(name, "BKLM.Layer%02d%sGasSolid", layer, (hasChimney ? "Chimney" : ""));
816  const CLHEP::Hep3Vector gasHalfSize = m_GeoPar->getGasHalfSize(layer, hasChimney) * CLHEP::cm;
817  G4Box* gasBox =
818  new G4Box(name,
819  gasHalfSize.x(), gasHalfSize.y(), gasHalfSize.z()
820  );
821  G4LogicalVolume* gasLogical =
822  new G4LogicalVolume(gasBox,
823  Materials::get("RPCGas"),
824  logicalName(gasBox),
825  0, // use global field manager
826  m_Sensitive, // this is the only sensitive volume in BKLM
827  0 // no user limits
828  );
829  m_VisAttributes.push_back(new G4VisAttributes(true));
830  m_VisAttributes.back()->SetColour(1.0, 0.5, 0.0);
831  gasLogical->SetVisAttributes(m_VisAttributes.back());
832  new G4PVPlacement(G4TranslateX3D(-0.5 * electrodeHalfSize.x()),
833  gasLogical,
834  physicalName(gasLogical),
835  electrodeLogical,
836  false,
837  BKLM_INNER,
838  false
839  );
840 
841  new G4PVPlacement(G4TranslateX3D(+0.5 * electrodeHalfSize.x()),
842  gasLogical,
843  physicalName(gasLogical),
844  electrodeLogical,
845  false,
846  BKLM_OUTER,
847  false
848  );
849  new G4PVPlacement(G4TranslateZ3D(0.0),
850  electrodeLogical,
851  physicalName(electrodeLogical),
852  interiorLogical,
853  false,
854  1,
855  false
856  );
857 }
858 
859 void GeoBKLMCreator::putScintsInInterior(G4LogicalVolume* interiorLogical, int section, int sector, int layer, bool hasChimney)
860 {
861  char name[80] = "";
862  sprintf(name, "BKLM.Layer%02d%sAirSolid", layer, (hasChimney ? "Chimney" : ""));
863  const CLHEP::Hep3Vector airHalfSize = m_GeoPar->getAirHalfSize(layer, hasChimney) * CLHEP::cm;
864  G4Box* airBox =
865  new G4Box(name,
866  airHalfSize.x(), airHalfSize.y(), airHalfSize.z()
867  );
868  G4LogicalVolume* airLogical =
869  new G4LogicalVolume(airBox,
870  Materials::get("G4_AIR"),
871  logicalName(airBox)
872  );
873  airLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
874  sprintf(name, "BKLM.Layer%02d%sScintEnvelopeSolid", layer, (hasChimney ? "Chimney" : ""));
875  double mppcHousingHalfLength = m_GeoPar->getMPPCHousingHalfLength() * CLHEP::cm;
876  const CLHEP::Hep3Vector envelopeHalfSize = m_GeoPar->getScintEnvelopeHalfSize(layer, hasChimney) * CLHEP::cm +
877  CLHEP::Hep3Vector(0.0, mppcHousingHalfLength, mppcHousingHalfLength);
878  G4Box* scintEnvelopeBox =
879  new G4Box(name,
880  envelopeHalfSize.x(), envelopeHalfSize.y(), envelopeHalfSize.z()
881  );
882  G4LogicalVolume* innerEnvelopeLogical =
883  new G4LogicalVolume(scintEnvelopeBox,
884  Materials::get("G4_AIR"),
885  logicalName(scintEnvelopeBox)
886  );
887  innerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
888  double scintHalfHeight = m_GeoPar->getScintHalfHeight() * CLHEP::cm;
889  double scintHalfWidth = m_GeoPar->getScintHalfWidth() * CLHEP::cm;
890 
891  int envelopeOffsetSign = m_GeoPar->getScintEnvelopeOffsetSign(layer);
892  const Module* module = m_GeoPar->findModule(section == BKLMElementNumbers::c_ForwardSection, sector, layer);
893  for (int scint = 1; scint <= m_GeoPar->getNPhiScints(layer); ++scint) {
894  double scintHalfLength = module->getPhiScintHalfLength(scint) * CLHEP::cm;
895  double scintPosition = module->getPhiScintPosition(scint) * CLHEP::cm;
896  G4LogicalVolume* scintLogical = getScintLogical(scintHalfHeight, scintHalfWidth, scintHalfLength,
897  mppcHousingHalfLength);
898  new G4PVPlacement(G4Translate3D(0.0, scintPosition + mppcHousingHalfLength * envelopeOffsetSign, 0.0),
899  scintLogical,
900  physicalName(scintLogical),
901  innerEnvelopeLogical,
902  false,
903  scint,
904  false
905  );
906  }
907  G4LogicalVolume* outerEnvelopeLogical =
908  new G4LogicalVolume(scintEnvelopeBox,
909  Materials::get("G4_AIR"),
910  logicalName(scintEnvelopeBox)
911  );
912  outerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
913  for (int scint = 1; scint <= m_GeoPar->getNZScints(hasChimney); ++scint) {
914  double scintHalfLength = module->getZScintHalfLength(scint) * CLHEP::cm;
915  double scintOffset = module->getZScintOffset(scint) * CLHEP::cm;
916  double scintPosition = module->getZScintPosition(scint) * CLHEP::cm;
917  G4LogicalVolume* scintLogical = getScintLogical(scintHalfHeight, scintHalfWidth, scintHalfLength,
918  mppcHousingHalfLength);
919  new G4PVPlacement(G4Translate3D(0.0, scintOffset, scintPosition - mppcHousingHalfLength) * G4RotateX3D(M_PI_2 * envelopeOffsetSign),
920  scintLogical,
921  physicalName(scintLogical),
922  outerEnvelopeLogical,
923  false,
924  scint,
925  false
926  );
927  }
928  CLHEP::Hep3Vector envelopeOffset = m_GeoPar->getScintEnvelopeOffset(layer, hasChimney) * CLHEP::cm +
929  CLHEP::Hep3Vector(0.0, -mppcHousingHalfLength, mppcHousingHalfLength);
930  new G4PVPlacement(G4Translate3D(-envelopeHalfSize.x(), envelopeOffset.y() * envelopeOffsetSign, envelopeOffset.z()),
931  innerEnvelopeLogical,
932  physicalName(innerEnvelopeLogical),
933  airLogical,
934  false,
935  BKLM_INNER,
936  false
937  );
938  new G4PVPlacement(G4Translate3D(+envelopeHalfSize.x(), envelopeOffset.y() * envelopeOffsetSign, envelopeOffset.z()),
939  outerEnvelopeLogical,
940  physicalName(outerEnvelopeLogical),
941  airLogical,
942  false,
943  BKLM_OUTER,
944  false
945  );
946 
947  // Place readout carriers and preamplifiers along module perimeter
948  double containerHalfSizeZ = m_GeoPar->getReadoutContainerHalfSize().z() * CLHEP::cm;
949  G4LogicalVolume* readoutContainerLogical = getReadoutContainerLogical();
950  for (int station = 1; station <= m_GeoPar->getNReadoutStation(); ++station) {
951  double stationPosition = m_GeoPar->getReadoutStationPosition(station) * CLHEP::cm;
952  G4Transform3D xform;
953  if (m_GeoPar->getReadoutStationIsPhi(station)) {
954  xform = G4Translate3D(0.0, stationPosition, airHalfSize.z() - containerHalfSizeZ);
955  } else {
956  xform = G4Translate3D(0.0,
957  (containerHalfSizeZ - airHalfSize.y()) * envelopeOffsetSign,
958  airHalfSize.z() + stationPosition
959  ) * G4RotateX3D(M_PI_2 * envelopeOffsetSign);
960  // Don't place all z-readout stations in chimney module.
961  if (fabs(xform.getTranslation().z()) > airHalfSize.z())
962  continue;
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 
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 
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 
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 }
@ c_ChimneySector
Chimney sector: BB3 in 1-based notation; BB2 in 0-based notation.
static constexpr int getMaximalLayerNumber()
Get maximal layer number (1-based).
static constexpr int getMaximalSectorNumber()
Get maximal sector number (1-based).
The Class for BKLM geometry.
KLM sensitive-detector class.
void putLayer1SupportInInnerVoid(G4LogicalVolume *, bool)
Put the layer-0 support plate into the inner region's air void (sectors 1..5 only)
void putChimneyInLayer(G4LogicalVolume *, int)
Put the solenoid's cooling chimney into the backward top sector.
void putScintsInInterior(G4LogicalVolume *, int, int, int, bool)
Put the scintillators into each detector module's interior (module is itself in an air gap)
void createGeometry(const BKLMGeometryPar &parameters, G4LogicalVolume &topVolume, geometry::GeometryTypes type)
Create the geometry from a parameter object.
~GeoBKLMCreator()
Destructor of the GeoBKLMCreator class.
void putEndsInEnvelope(G4LogicalVolume *)
Put the forward and backward ends into the BKLM envelope.
G4LogicalVolume * getReadoutContainerLogical(void)
Get pointer to readout-container logical volume.
void putLayersInSector(G4LogicalVolume *, int, int, bool)
Put the layers into each sector.
GeoBKLMCreator()
Constructor of the GeoBKLMCreator class.
void putSectorsInEnd(G4LogicalVolume *, int)
Put each sector into the forward or backward end.
G4Tubs * getSolenoidTube(void)
Get shape corresponding to the solenoid (for subtraction)
void putCapInSector(G4LogicalVolume *, bool)
Put the cap (at maximum |z|) into each sector.
void putVoidInInnerRegion(G4LogicalVolume *, bool, bool)
Put the air void into the inner-radius region.
G4String logicalName(G4VSolid *)
convert G4VSolid's name to corresponding G4LogicalVolume name
G4LogicalVolume * getScintLogical(double, double, double, double)
Get pointer to scintillator logical volume.
G4String physicalName(G4LogicalVolume *)
convert G4LogicalVolume's name to corresponding G4PhysicalVolume name
void putRPCsInInterior(G4LogicalVolume *, int, bool)
Put the RPCs into each detector module's interior (module is itself in an air gap)
void putModuleInLayer(G4LogicalVolume *, int, int, int, bool, bool, int)
Put the module (and enclosing air gap) into each layer.
void putLayer1BracketsInInnerVoid(G4LogicalVolume *, bool)
Put the layer-0 support plate's brackets into the inner region's air void (sectors 1....
void putInnerRegionInSector(G4LogicalVolume *, bool, bool)
Put the inner-radius region into each sector.
G4LogicalVolume * getMPPCHousingLogical(void)
Get pointer to MPPC-container logical volume.
static GeometryPar * instance(void)
Static method to get a reference to the singleton GeometryPar instance.
Definition: GeometryPar.cc:27
Define the geometry of a BKLM module Each sector [octant] contains Modules.
Definition: Module.h:76
static G4Material * get(const std::string &name)
Find given material.
Definition: Materials.h:63
double tan(double a)
tan for double
Definition: beamHelpers.h:31
Common code concerning the geometry representation of the detector.
Definition: CreatorBase.h:25
GeometryTypes
Flag indiciating the type of geometry to be used.