Belle II Software development
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
34using namespace Belle2::bklm;
35using 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;
52 m_InnerAirSolid = 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;
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
95void 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
135void 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),
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
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
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
178void GeoBKLMCreator::putSectorsInEnd(G4LogicalVolume* endLogical, int section)
179{
180 if (m_SectorTube == nullptr) {
182 new G4Tubs("BKLM.SectorSolid",
183 m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
184 m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
186 -0.5 * 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
216void 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,
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
283void 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,
296 1,
297 2, z, rInner, rOuter
298 );
300 new G4SubtractionSolid("BKLM.InnerIronSolid",
301 innerIronPolygon,
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],
318 sectorLogical,
319 false,
320 1,
321 false
322 );
323}
324
325void 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,
339 1,
340 2, z, rInner, rOuter
341 );
343 new G4SubtractionSolid("BKLM.InnerAirSolid",
344 innerAirPolygon,
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) {
360 }
361 }
362 new G4PVPlacement(G4TranslateX3D(m_RibShift),
363 m_InnerAirLogical[newLvol],
365 innerIronLogical,
366 false,
367 1,
368 false
369 );
370}
371
372void 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],
396 innerAirLogical,
397 false,
398 1,
399 false
400 );
401}
402
403void 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,
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,
441 1,
442 2, z2, rInner, rOuter2
443 );
444 G4Polyhedra* bracketCutout3 =
445 new G4Polyhedra("BKLM.BracketCutout3",
446 -0.5 * 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 );
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),
492 name,
493 innerAirLogical,
494 false,
495 bracket,
496 false
497 );
498 }
499}
500
501void 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,
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],
551 sectorLogical,
552 false,
553 layer,
554 false
555 );
556
557 }
558}
559
560void 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",
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",
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
697void 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],
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],
791 layerIronLogical,
792 false,
793 layer,
794 false
795 );
796}
797
798void 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
859void 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
986G4LogicalVolume* GeoBKLMCreator::getScintLogical(double dx, double dy, double dz, double dzMPPC)
987{
988
989 int newLvol = 1;
990 for (G4LogicalVolume* logicalVolume : m_ScintLogicals) {
991 G4Box* box = (G4Box*)(logicalVolume->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)) {
995 return logicalVolume;
996 }
997 newLvol++;
998 }
999 char name[80] = "";
1000 sprintf(name, "BKLM.ScintType%dSolid", newLvol);
1001 G4Box* scintBox = new G4Box(name, dx, dy, dz + dzMPPC);
1002 G4LogicalVolume* scintLogical =
1003 new G4LogicalVolume(scintBox, Materials::get("G4_POLYSTYRENE"), logicalName(scintBox));
1004 m_ScintLogicals.push_back(scintLogical);
1005 scintLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1006 sprintf(name, "BKLM.ScintType%dAirSolid", newLvol);
1007 G4Box* scintAirBox = new G4Box(name, dx, dy, dzMPPC);
1008 G4LogicalVolume* scintAirLogical =
1009 new G4LogicalVolume(scintAirBox, Materials::get("G4_AIR"), logicalName(scintAirBox));
1010 scintAirLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1011 double dxTiO2 = m_GeoPar->getScintTiO2ThicknessTop() * CLHEP::cm;
1012 double dyTiO2 = m_GeoPar->getScintTiO2ThicknessSide() * CLHEP::cm;
1013 sprintf(name, "BKLM.ScintActiveType%dSolid", newLvol);
1014 G4Box* activeBox = new G4Box(name, dx - dxTiO2, dy - dyTiO2, dz);
1015 G4LogicalVolume* activeLogical =
1016 new G4LogicalVolume(activeBox, Materials::get("G4_POLYSTYRENE"), logicalName(activeBox), 0, m_Sensitive, 0);
1017 m_VisAttributes.push_back(new G4VisAttributes(true));
1018 m_VisAttributes.back()->SetColour(1.0, 0.5, 0.0);
1019 activeLogical->SetVisAttributes(m_VisAttributes.back());
1020 sprintf(name, "BKLM.ScintBoreType%dSolid", newLvol);
1021 G4Tubs* boreTube = new G4Tubs(name, 0.0, m_GeoPar->getScintBoreRadius() * CLHEP::cm, dz, 0.0, 2.0 * M_PI);
1022 G4LogicalVolume* scintBoreLogical =
1023 new G4LogicalVolume(boreTube, Materials::get("G4_AIR"), logicalName(boreTube));
1024 scintBoreLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1025 sprintf(name, "BKLM.ScintFiberType%dSolid", newLvol);
1026 G4Tubs* fiberTube = new G4Tubs(name, 0.0, m_GeoPar->getScintFiberRadius() * CLHEP::cm, dz, 0.0, 2.0 * M_PI);
1027 G4LogicalVolume* scintFiberLogical = new G4LogicalVolume(fiberTube, Materials::get("G4_POLYSTYRENE"), logicalName(fiberTube));
1028 m_VisAttributes.push_back(new G4VisAttributes(true));
1029 m_VisAttributes.back()->SetColour(0.0, 1.0, 0.0);
1030 scintFiberLogical->SetVisAttributes(m_VisAttributes.back());
1031 new G4PVPlacement(G4TranslateZ3D(0.0),
1032 scintFiberLogical,
1033 physicalName(scintFiberLogical),
1034 scintBoreLogical,
1035 false,
1036 1,
1037 false
1038 );
1039 new G4PVPlacement(G4TranslateZ3D(0.0),
1040 scintBoreLogical,
1041 physicalName(scintBoreLogical),
1042 activeLogical,
1043 false,
1044 1,
1045 false
1046 );
1047 new G4PVPlacement(G4TranslateZ3D(-dzMPPC),
1048 activeLogical,
1049 physicalName(activeLogical),
1050 scintLogical,
1051 false,
1052 1,
1053 false
1054 );
1055 new G4PVPlacement(G4TranslateZ3D(0.0),
1057 "BKLM.MPPCHousingPhysical",
1058 scintAirLogical,
1059 false,
1060 1,
1061 false
1062 );
1063 new G4PVPlacement(G4TranslateZ3D(dz),
1064 scintAirLogical,
1065 physicalName(scintAirLogical),
1066 scintLogical,
1067 false,
1068 1,
1069 false
1070 );
1071 return scintLogical;
1072}
1073
1075{
1076 if (m_MPPCHousingLogical == nullptr) {
1077 G4Tubs* mppcHousingSolid =
1078 new G4Tubs("BKLM.MPPCHousingSolid",
1079 0.0,
1080 m_GeoPar->getMPPCHousingRadius() * CLHEP::cm,
1081 m_GeoPar->getMPPCHousingHalfLength() * CLHEP::cm,
1082 0.0,
1083 2.0 * M_PI
1084 );
1086 new G4LogicalVolume(mppcHousingSolid,
1087 Materials::get("G4_POLYCARBONATE"),
1088 "BKLM>MPPCHousingLogical"
1089 );
1090 m_VisAttributes.push_back(new G4VisAttributes(true));
1091 m_VisAttributes.back()->SetColour(0.5, 0.5, 0.5); // gray
1092 m_MPPCHousingLogical->SetVisAttributes(m_VisAttributes.back());
1093 G4Box* mppcBox =
1094 new G4Box("BKLM.MPPCSolid",
1095 m_GeoPar->getMPPCHalfLength() * CLHEP::cm,
1096 m_GeoPar->getMPPCHalfWidth() * CLHEP::cm,
1097 m_GeoPar->getMPPCHalfHeight() * CLHEP::cm
1098 );
1099 G4LogicalVolume* mppcLogical =
1100 new G4LogicalVolume(mppcBox,
1101 Materials::get("G4_Si"),
1102 "BKLM.MPPCLogical"
1103 );
1104 m_VisAttributes.push_back(new G4VisAttributes(true));
1105 m_VisAttributes.back()->SetColour(1.0, 1.0, 1.0); // white
1106 mppcLogical->SetVisAttributes(m_VisAttributes.back());
1107 new G4PVPlacement(G4TranslateX3D(0.0),
1108 mppcLogical,
1109 "BKLM.MPPCPhysical",
1111 false,
1112 1,
1113 false
1114 );
1115 }
1116 return m_MPPCHousingLogical;
1117}
1118
1120{
1121 if (m_ReadoutContainerLogical == nullptr) {
1122 const CLHEP::Hep3Vector containerHalfSize = m_GeoPar->getReadoutContainerHalfSize() * CLHEP::cm;
1123 G4Box* containerBox =
1124 new G4Box("BKLM.ReadoutContainerSolid",
1125 containerHalfSize.x(), containerHalfSize.y(), containerHalfSize.z()
1126 );
1128 new G4LogicalVolume(containerBox,
1129 Materials::get("G4_AIR"),
1130 logicalName(containerBox)
1131 );
1132 m_ReadoutContainerLogical->SetVisAttributes(m_VisAttributes.front()); // invisible
1133
1134 const CLHEP::Hep3Vector carrierHalfSize = m_GeoPar->getReadoutCarrierHalfSize() * CLHEP::cm;
1135 G4Box* carrierBox =
1136 new G4Box("BKLM.ReadoutCarrierSolid",
1137 carrierHalfSize.x(), carrierHalfSize.y(), carrierHalfSize.z()
1138 );
1139 G4LogicalVolume* carrierLogical =
1140 new G4LogicalVolume(carrierBox,
1141 Materials::get("NEMA_G10_Plate"), // defined in CDC
1142 logicalName(carrierBox)
1143 );
1144 m_VisAttributes.push_back(new G4VisAttributes(true));
1145 m_VisAttributes.back()->SetColour(0.0, 1.0, 0.0);
1146 carrierLogical->SetVisAttributes(m_VisAttributes.back());
1147 const CLHEP::Hep3Vector preamplifierHalfSize = m_GeoPar->getReadoutPreamplifierHalfSize() * CLHEP::cm;
1148 G4Box* preamplifierBox =
1149 new G4Box("BKLM.ReadoutPreamplifierSolid",
1150 preamplifierHalfSize.x(), preamplifierHalfSize.y(), preamplifierHalfSize.z()
1151 );
1152 G4LogicalVolume* preamplifierLogical =
1153 new G4LogicalVolume(preamplifierBox,
1154 Materials::get("NEMA_G10_Plate"), // defined in CDC
1155 logicalName(preamplifierBox)
1156 );
1157 preamplifierLogical->SetVisAttributes(m_VisAttributes.back());
1158 const CLHEP::Hep3Vector connectorsHalfSize = m_GeoPar->getReadoutConnectorsHalfSize() * CLHEP::cm;
1159 G4Box* connectorsBox =
1160 new G4Box("BKLM.ReadoutConnectorsSolid",
1161 connectorsHalfSize.x(), connectorsHalfSize.y(), connectorsHalfSize.z()
1162 );
1163 G4LogicalVolume* connectorsLogical =
1164 new G4LogicalVolume(connectorsBox,
1165 Materials::get("G4_POLYCARBONATE"),
1166 logicalName(connectorsBox)
1167 );
1168 m_VisAttributes.push_back(new G4VisAttributes(true));
1169 m_VisAttributes.back()->SetColour(0.5, 0.5, 0.5);
1170 connectorsLogical->SetVisAttributes(m_VisAttributes.back());
1171 new G4PVPlacement(G4TranslateZ3D(preamplifierHalfSize.z()),
1172 carrierLogical,
1173 physicalName(carrierLogical),
1175 false,
1176 1,
1177 false
1178 );
1179 new G4PVPlacement(G4Translate3D(0.0, m_GeoPar->getReadoutConnectorsPosition(), -carrierHalfSize.z()),
1180 connectorsLogical,
1181 physicalName(connectorsLogical),
1183 false,
1184 1,
1185 false
1186 );
1187 for (int preamp = 1; preamp <= m_GeoPar->getNReadoutPreamplifierPosition(); ++preamp) {
1188 new G4PVPlacement(G4Translate3D(0.0,
1189 m_GeoPar->getReadoutPreamplifierPosition(preamp) * CLHEP::cm,
1190 -carrierHalfSize.z()),
1191 preamplifierLogical,
1192 physicalName(preamplifierLogical),
1194 false,
1195 1,
1196 false
1197 );
1198 }
1199 }
1201}
1202
1204{
1205
1206 if (m_SolenoidTube == nullptr) {
1208 new G4Tubs("BKLM.SolenoidTube",
1209 0.0,
1210 m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
1211 2.0 * m_GeoPar->getHalfLength() * CLHEP::cm,
1212 0.0,
1213 2.0 * M_PI
1214 );
1215 }
1216 return m_SolenoidTube;
1217}
1218
1219G4String GeoBKLMCreator::logicalName(G4VSolid* solid)
1220{
1221 G4String* name = new G4String(solid->GetName().substr(0, solid->GetName().size() - 5) + "Logical");
1222 m_Names.push_back(name);
1223 return *name;
1224}
1225
1226G4String GeoBKLMCreator::physicalName(G4LogicalVolume* lvol)
1227{
1228 G4String* name = new G4String(lvol->GetName().substr(0, lvol->GetName().size() - 7) + "Physical");
1229 m_Names.push_back(name);
1230 return *name;
1231}
@ 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.
G4LogicalVolume * m_LayerGapLogical[12 *BKLMElementNumbers::getMaximalLayerNumber()]
Pointers to logical volumes for air gap in each layer [side/bottom/top | isFlipped | hasChimney | lay...
G4LogicalVolume * m_SupportLogical[2]
Pointer to logical volumes for support structure [hasChimney].
G4Tubs * m_SolenoidTube
Pointer to solid for solenoid.
G4LogicalVolume * m_ReadoutContainerLogical
Pointer to logical volume for scint preamplifier/carrier container.
void putScintsInInterior(G4LogicalVolume *, int, int, int, bool)
Put the scintillators into each detector module's interior (module is itself in an air gap)
G4LogicalVolume * m_BracketLogical
Pointer to logical volume for bracket.
G4Polyhedra * m_CapSolid
Pointer to solid for cap.
G4LogicalVolume * m_SectorLogical[2][BKLMElementNumbers::getMaximalSectorNumber()]
Pointers to logical volumes for each sector [fb-1][sector-1].
void createGeometry(const BKLMGeometryPar &parameters, G4LogicalVolume &topVolume, geometry::GeometryTypes type)
Create the geometry from a parameter object.
double m_SectorDphi
Angular extent of one sector.
G4Tubs * m_SectorTube
Pointer to solid for sector's enclosing tube.
~GeoBKLMCreator()
Destructor of the GeoBKLMCreator class.
void putEndsInEnvelope(G4LogicalVolume *)
Put the forward and backward ends into the BKLM envelope.
G4Box * m_LayerGapSolid[2 *BKLMElementNumbers::getMaximalLayerNumber()]
Pointers to solids for air gap in each layer [hasChimney | layer-1].
double m_RibShift
Radial displacement of polygon to create an azimuthal iron rib.
G4LogicalVolume * getReadoutContainerLogical(void)
Get pointer to readout-container logical volume.
void putLayersInSector(G4LogicalVolume *, int, int, bool)
Put the layers into each sector.
std::vector< G4VisAttributes * > m_VisAttributes
Vector of pointers to G4VisAttributes objects.
G4LogicalVolume * m_MPPCHousingLogical
Pointer to logical volume for MPPC housing.
GeoBKLMCreator()
Constructor of the GeoBKLMCreator class.
G4VSolid * m_InnerIronSolid
Pointer to solid for inner iron [hasInnerSupport | hasChimney].
void putSectorsInEnd(G4LogicalVolume *, int)
Put each sector into the forward or backward end.
G4Tubs * getSolenoidTube(void)
Get shape corresponding to the solenoid (for subtraction)
double m_SectorDz
Half-length of one sector.
G4Polyhedra * m_LayerIronSolid[BKLMElementNumbers::getMaximalLayerNumber()]
Pointers to solids for iron in each layer [layer-1].
G4LogicalVolume * m_InnerAirLogical[4]
Pointer to logical volumes for inner air [hasInnerSupport | hasChimney].
G4LogicalVolume * m_CapLogical[2]
Pointer to logical volumes for cap [hasChimney].
void putCapInSector(G4LogicalVolume *, bool)
Put the cap (at maximum |z|) into each sector.
G4LogicalVolume * m_InnerIronLogical[4]
Pointer to logical volumes for inner iron [hasInnerSupport | hasChimney].
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
G4VSensitiveDetector * m_Sensitive
Pointer to the BKLM SensitiveDetector processor.
G4LogicalVolume * getScintLogical(double, double, double, double)
Get pointer to scintillator logical volume.
std::vector< G4LogicalVolume * > m_ScintLogicals
Pointers to logical volumes for scintillator strips.
G4LogicalVolume * m_LayerIronLogical[12 *BKLMElementNumbers::getMaximalLayerNumber()]
Pointers to logical volumes for iron in each layer [side/bottom/top | isFlipped | hasChimney | layer-...
GeometryPar * m_GeoPar
Pointer to the BKLM geometry accessor.
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.
G4VSolid * m_InnerAirSolid
Pointer to solid for inner air.
std::vector< G4String * > m_Names
Vector of pointers to G4String objects (volume names)
G4LogicalVolume * getMPPCHousingLogical(void)
Get pointer to MPPC-container logical volume.
G4LogicalVolume * m_LayerModuleLogical[2 *BKLMElementNumbers::getMaximalLayerNumber()]
Pointers to logical volumes for detector modules in each layer's air gap [hasChimney | layer-1].
double getScintBoreRadius(void) const
Get the radius of the cylindrical central bore in a scintillator strip.
Definition: GeometryPar.h:124
double getBracketThickness(void) const
Get the thickness of the layer-0 support plate's bracket.
Definition: GeometryPar.h:454
const CLHEP::Hep3Vector getReadoutContainerHalfSize(void) const
Get the size (dx,dy,dz) of the readout container.
Definition: GeometryPar.cc:682
double getMPPCHousingRadius(void) const
Get the MPPC housing radius.
Definition: GeometryPar.h:541
double getLayerOuterRadius(int layer) const
Get the outer radius of specified layer.
Definition: GeometryPar.cc:500
double getChimneyShieldInnerRadius(void) const
Get the inner radius of the chimney radiation shield.
Definition: GeometryPar.h:397
const CLHEP::Hep3Vector getModuleHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the detector module of specified layer.
Definition: GeometryPar.cc:522
double getScintHalfHeight(void) const
Get the height of the entire volume of a scintillator strip (including TiO2 coating)
Definition: GeometryPar.h:112
const CLHEP::Hep3Vector getModuleInteriorHalfSize1(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the detector module's interior volume 1.
Definition: GeometryPar.cc:530
const Module * findModule(int section, int sector, int layer) const
Get the pointer to the definition of a module.
Definition: GeometryPar.cc:721
double getScintTiO2ThicknessTop(void) const
Get the thickness of the inactive TiO2-polystyrene coating on top (broad) surface of a scintillator s...
Definition: GeometryPar.h:100
int getNLayer(void) const
Get the number of modules in one sector.
Definition: GeometryPar.h:193
int getNPhiScints(int layer) const
Get the number of phi-measuring scintillators in a scintillator module.
Definition: GeometryPar.cc:579
double getScintHalfWidth(void) const
Get the height of the entire volume of a scintillator strip (including TiO2 coating)
Definition: GeometryPar.h:118
const CLHEP::Hep3Vector getScintEnvelopeOffset(int layer, bool hasChimney) const
Get the shift (dx,dy,dz) of the scintillator detector module's scintillator envelope within its enclo...
Definition: GeometryPar.cc:633
bool hasRPCs(int layer) const
Determine if the sensitive detectors in a given layer are RPCs (=true) or scintillators (=false)
Definition: GeometryPar.cc:714
double getMPPCHousingHalfLength(void) const
Get the MPPC housing half-length.
Definition: GeometryPar.h:547
int getNReadoutPreamplifierPosition(void) const
Get the number of preamplifier positions along the length of the carrier card.
Definition: GeometryPar.h:520
int getNReadoutStation(void) const
Get the number of preamplifier readout stations.
Definition: GeometryPar.h:493
double getScintFiberRadius(void) const
Get the radius of the cylindrical central WLS fiber in a scintillator strip.
Definition: GeometryPar.h:130
const CLHEP::Hep3Vector getScintEnvelopeHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the scintillator detector module's scintillator envelope.
Definition: GeometryPar.cc:571
const CLHEP::Hep3Vector getChimneyPosition(int layer) const
Get the position of the chimney hole in the specified layer.
Definition: GeometryPar.cc:650
double getBracketWidth(void) const
Get the width of the layer-0 support plate's bracket.
Definition: GeometryPar.h:448
double getOuterRadius(void) const
Get the radius of the inscribed circle of the outer polygon.
Definition: GeometryPar.h:187
double getOffsetZ(void) const
Get the global shift along a of the entire BKLM.
Definition: GeometryPar.h:157
double getBracketZPosition(int, bool) const
Get the position of a layer-0 support plate's bracket.
Definition: GeometryPar.cc:670
double getReadoutConnectorsPosition(void) const
Get the position of the readout connectors pair along the length of the carrier card.
Definition: GeometryPar.h:535
double getChimneyPipeOuterRadius(void) const
Get the outer radius of the chimney pipe.
Definition: GeometryPar.h:415
bool getReadoutStationIsPhi(int station) const
Get the selector for phi (true) or z (false) readout station.
Definition: GeometryPar.h:499
const CLHEP::Hep3Vector getReadoutPreamplifierHalfSize(void) const
Get the size (dx,dy,dz) of the preamplifier card.
Definition: GeometryPar.cc:698
double getBracketLength(void) const
Get the length of the layer-0 support plate's bracket.
Definition: GeometryPar.h:460
int getNZScints(bool isChimney) const
Get the number of z-measuring scintillators in a scintillator module.
Definition: GeometryPar.h:274
double getPolystyreneOffsetX(void) const
Get the radial offset of the scintillator detector module's active envelope due to difference in poly...
Definition: GeometryPar.cc:586
double getReadoutStationPosition(int station) const
Get the position of each readout station.
Definition: GeometryPar.h:505
const CLHEP::Hep3Vector getReadoutConnectorsHalfSize(void) const
Get the size (dx,dy,dz) of the readout connectors pair.
Definition: GeometryPar.cc:706
double getRibThickness(void) const
Get the thickness of the radial rib that supports the solenoid / inner detectors.
Definition: GeometryPar.h:421
int getNSector(void) const
Get the number of sectors of the BKLM.
Definition: GeometryPar.h:175
const CLHEP::Hep3Vector getModuleInteriorHalfSize2(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the scintillator detector module's polystyrene filler.
Definition: GeometryPar.cc:539
double getRotation(void) const
Get the global rotation angle about z of the entire BKLM.
Definition: GeometryPar.h:151
double getHalfLength(void) const
Get the half-length along z of the BKLM.
Definition: GeometryPar.h:181
const CLHEP::Hep3Vector getGapHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the gap [=slot] of specified layer.
Definition: GeometryPar.cc:505
double getBraceWidthChimney(void) const
Get the width of the brace in the middle of the cable-services channel in the chimney sector.
Definition: GeometryPar.h:439
double getBracketRibThickness(void) const
Get the thickness of the layer-0 support plate's bracket's rib.
Definition: GeometryPar.h:472
double getChimneyHousingInnerRadius(void) const
Get the inner radius of the chimney housing.
Definition: GeometryPar.h:385
double getMPPCHalfLength(void) const
Get the MPPC half-length.
Definition: GeometryPar.h:553
const CLHEP::Hep3Vector getSupportPlateHalfSize(bool) const
Get the size of the layer-0 support plate.
Definition: GeometryPar.cc:657
const CLHEP::Hep3Vector getChimneyHalfSize(int layer) const
Get the size of the chimney hole in the specified layer.
Definition: GeometryPar.cc:643
double getCablesWidth(void) const
Get the width of the cable-services channel at each end.
Definition: GeometryPar.h:427
double getGapMiddleRadius(int layer) const
Get the radial midpoint of the gap of specified layer.
Definition: GeometryPar.cc:591
double getBracketCutoutDphi(void) const
Get the angular width of the layer-0 support plate's bracket's cutout.
Definition: GeometryPar.h:487
double getChimneyShieldOuterRadius(void) const
Get the outer radius of the chimney radiation shield.
Definition: GeometryPar.h:403
double getChimneyCoverThickness(void) const
Get the thickness of the chimney cover plate.
Definition: GeometryPar.h:379
double getReadoutPreamplifierPosition(int preamp) const
Get the position of a preamplifier along the length of the carrier card.
Definition: GeometryPar.h:526
double getBraceWidth(void) const
Get the width of the brace in the middle of the cable-services channel.
Definition: GeometryPar.h:433
const CLHEP::Hep3Vector getReadoutCarrierHalfSize(void) const
Get the size (dx,dy,dz) of the carrier card.
Definition: GeometryPar.cc:690
const CLHEP::Hep3Vector getGasHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the detector module's gas gaps of specified layer.
Definition: GeometryPar.cc:555
const CLHEP::Hep3Vector getElectrodeHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the detector module's electrode of specified layer.
Definition: GeometryPar.cc:548
static GeometryPar * instance(void)
Static method to get a reference to the singleton GeometryPar instance.
Definition: GeometryPar.cc:27
int getScintEnvelopeOffsetSign(int layer) const
Get the sign (+/-1) of scintillator-envelope's shift along y axis within its enclosing module for MPP...
Definition: GeometryPar.h:91
double getModuleMiddleRadius(int layer) const
Get the radial midpoint of the detector module of specified layer.
Definition: GeometryPar.cc:599
double getMPPCHalfWidth(void) const
Get the MPPC half-width.
Definition: GeometryPar.h:559
double getMPPCHalfHeight(void) const
Get the MPPC half-height.
Definition: GeometryPar.h:565
double getGapLength(void) const
Get the length along z of the module gap.
Definition: GeometryPar.h:235
double getScintTiO2ThicknessSide(void) const
Get the thickness of the inactive TiO2-polystyrene coating on side (short) surface of a scintillator ...
Definition: GeometryPar.h:106
double getSolenoidOuterRadius(void) const
Get the outer radius of the solenoid.
Definition: GeometryPar.h:169
double getChimneyPipeInnerRadius(void) const
Get the inner radius of the chimney pipe.
Definition: GeometryPar.h:409
const HepGeom::Transform3D getModuleDisplacedGeo(int section, int sector, int layer) const
Get the displacement transformation of a module.
Definition: GeometryPar.cc:735
double getBracketInnerRadius(void) const
Get the inner radius of the layer-0 support plate's bracket.
Definition: GeometryPar.h:478
double getLayerInnerRadius(int layer) const
Get the inner radius of specified layer.
Definition: GeometryPar.cc:489
double getChimneyHousingOuterRadius(void) const
Get the outer radius of the chimney housing.
Definition: GeometryPar.h:391
const CLHEP::Hep3Vector getAirHalfSize(int layer, bool hasChimney) const
Get the size (dx,dy,dz) of the scintillator detector module's air filler.
Definition: GeometryPar.cc:564
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
Common code concerning the geometry representation of the detector.
Definition: CreatorBase.h:25
GeometryTypes
Flag indiciating the type of geometry to be used.