10 #include <klm/bklm/geometry/GeoBKLMCreator.h>
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>
19 #include <geometry/Materials.h>
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>
31 #include <G4UnionSolid.hh>
32 #include <G4VisAttributes.hh>
34 using namespace Belle2::bklm;
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;
57 m_LayerIronSolid[j] =
nullptr;
60 m_LayerModuleLogical[j] =
nullptr;
61 m_LayerGapSolid[j] =
nullptr;
64 m_LayerIronLogical[j] =
nullptr;
65 m_LayerGapLogical[j] =
nullptr;
67 m_SectorTube =
nullptr;
69 m_SectorLogical[0][sector] =
nullptr;
70 m_SectorLogical[1][sector] =
nullptr;
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));
84 m_ScintLogicals.clear();
85 for (G4VisAttributes* visAttr : m_VisAttributes)
delete visAttr;
86 m_VisAttributes.clear();
87 for (G4String* name : m_Names)
delete name;
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);
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),
112 G4LogicalVolume* envelopeLogical =
113 new G4LogicalVolume(envelopeSolid,
115 "BKLM.EnvelopeLogical"
117 envelopeLogical->SetVisAttributes(m_VisAttributes.front());
118 putEndsInEnvelope(envelopeLogical);
120 G4Region* aRegion =
new G4Region(
"BKLMEnvelope");
121 envelopeLogical->SetRegion(aRegion);
122 aRegion->AddRootLogicalVolume(envelopeLogical);
124 new G4PVPlacement(G4TranslateZ3D(m_GeoPar->getOffsetZ() * CLHEP::cm) * G4RotateZ3D(m_GeoPar->getRotation() * CLHEP::rad),
126 "BKLM.EnvelopePhysical",
138 new G4Tubs(
"BKLM.EndSolid",
139 m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
140 m_GeoPar->getOuterRadius() * CLHEP::cm / cos(0.5 * m_SectorDphi),
145 G4LogicalVolume* frontLogical =
146 new G4LogicalVolume(endSolid,
150 frontLogical->SetVisAttributes(m_VisAttributes.front());
152 new G4PVPlacement(G4TranslateZ3D(m_SectorDz),
160 G4LogicalVolume* backLogical =
161 new G4LogicalVolume(endSolid,
165 backLogical->SetVisAttributes(m_VisAttributes.front());
167 new G4PVPlacement(G4TranslateZ3D(-m_SectorDz) * G4RotateY3D(M_PI),
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),
191 for (
int s = 0; s < m_GeoPar->getNSector(); ++s) {
194 bool hasInnerSupport = (sector <= m_GeoPar->getNSector() / 2 + 1);
196 m_SectorLogical[section][sector - 1] =
197 new G4LogicalVolume(m_SectorTube,
201 m_SectorLogical[section][sector - 1]->SetVisAttributes(m_VisAttributes.front());
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]),
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};
231 new G4Polyhedra(
"BKLM.CapSolid",
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,
246 m_CapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
247 name = (hasChimney ?
"BKLM.ChimneyCablesSolid" :
"BKLM.CablesSolid");
249 new G4Box(name, 0.5 * (ro - ri), dy, dz);
250 G4LogicalVolume* cablesLogical =
251 new G4LogicalVolume(cablesSolid,
253 logicalName(cablesSolid)
255 cablesLogical->SetVisAttributes(m_VisAttributes.front());
256 new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), -(0.5 * dyBrace + dy), 0.0),
258 physicalName(cablesLogical).append(G4String(
"_L")),
259 m_CapLogical[newLvol],
264 new G4PVPlacement(G4Translate3D(0.5 * (ri + ro), +(0.5 * dyBrace + dy), 0.0),
266 physicalName(cablesLogical).append(G4String(
"_R")),
267 m_CapLogical[newLvol],
273 new G4PVPlacement(G4TranslateZ3D(m_SectorDz - dz),
274 m_CapLogical[newLvol],
275 physicalName(m_CapLogical[newLvol]),
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",
300 new G4SubtractionSolid(
"BKLM.InnerIronSolid",
305 int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
306 if (m_InnerIronLogical[newLvol] ==
nullptr) {
307 m_InnerIronLogical[newLvol] =
308 new G4LogicalVolume(m_InnerIronSolid,
310 (hasChimney ?
"BKLM.InnerIronChimneyLogical" :
"BKLM.InnerIronLogical")
312 m_InnerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
313 putVoidInInnerRegion(m_InnerIronLogical[newLvol], hasInnerSupport, hasChimney);
315 new G4PVPlacement(G4TranslateZ3D(0.0),
316 m_InnerIronLogical[newLvol],
317 physicalName(m_InnerIronLogical[newLvol]),
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",
343 new G4SubtractionSolid(
"BKLM.InnerAirSolid",
346 G4TranslateX3D(-m_RibShift)
349 int newLvol = (hasInnerSupport ? 2 : 0) + (hasChimney ? 1 : 0);
350 if (m_InnerAirLogical[newLvol] ==
nullptr) {
351 m_InnerAirLogical[newLvol] =
352 new G4LogicalVolume(m_InnerAirSolid,
354 (hasChimney ?
"BKLM.InnerAirChimneyLogical" :
"BKLM.InnerAirLogical")
356 m_InnerAirLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
357 if (hasInnerSupport) {
358 putLayer1SupportInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
359 putLayer1BracketsInInnerVoid(m_InnerAirLogical[newLvol], hasChimney);
362 new G4PVPlacement(G4TranslateX3D(m_RibShift),
363 m_InnerAirLogical[newLvol],
364 physicalName(m_InnerAirLogical[newLvol]),
375 int newLvol = (hasChimney ? 1 : 0);
376 const CLHEP::Hep3Vector size = m_GeoPar->getSupportPlateHalfSize(hasChimney) * CLHEP::cm;
377 if (m_SupportLogical[newLvol] ==
nullptr) {
379 new G4Box((hasChimney ?
"BKLM.ChimneySupportSolid" :
"BKLM.SupportSolid"),
384 m_SupportLogical[newLvol] =
385 new G4LogicalVolume(supportBox,
387 logicalName(supportBox)
389 m_SupportLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
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]),
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",
430 G4Polyhedra* bracketCutout1 =
431 new G4Polyhedra(
"BKLM.BracketCutout1",
435 2, z1, rInner, rOuter1
437 G4Polyhedra* bracketCutout2 =
438 new G4Polyhedra(
"BKLM.BracketCutout2",
442 2, z2, rInner, rOuter2
444 G4Polyhedra* bracketCutout3 =
445 new G4Polyhedra(
"BKLM.BracketCutout3",
449 2, z3, rInner, rOuter2
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
458 new G4SubtractionSolid(
"BKLM.BracketPolygon1",
463 new G4SubtractionSolid(
"BKLM.BracketPolygon2",
468 new G4SubtractionSolid(
"BKLM.BracketPolygon3",
471 G4TranslateX3D(bracketShift)
473 G4VSolid* bracketSolid =
474 new G4SubtractionSolid(
"BKLM.BracketSolid",
479 new G4LogicalVolume(bracketSolid,
481 "BKLM.BracketLogical"
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());
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),
504 const double dz = 0.5 * m_GeoPar->getGapLength() * CLHEP::cm;
505 const double z[2] = { -dz, +dz };
507 for (
int layer = 1; layer <= m_GeoPar->getNLayer(); ++layer) {
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,
524 bool isFlipped = module->isFlipped();
526 (hasChimney ? 1 : 0)) + (layer - 1);
528 int s2 = m_GeoPar->getNSector() / 2;
530 }
else if (s1 > s2) {
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],
542 m_LayerIronLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
543 putModuleInLayer(m_LayerIronLogical[newLvol], section, sector, layer, hasChimney, isFlipped, newLvol);
545 putChimneyInLayer(m_LayerIronLogical[newLvol], layer);
548 new G4PVPlacement(G4TranslateZ3D(dz - m_SectorDz),
549 m_LayerIronLogical[newLvol],
550 physicalName(m_LayerIronLogical[newLvol]),
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();
572 sprintf(name,
"BKLM.Layer%02dGapChimneyBox", layer);
575 gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
577 sprintf(name,
"BKLM.Layer%02dGapChimneyLogical", layer);
578 G4LogicalVolume* gapLogical =
579 new G4LogicalVolume(gapBox,
583 gapLogical->SetVisAttributes(m_VisAttributes.front());
584 sprintf(name,
"BKLM.Layer%02dLeftGapChimneyPhysical", layer);
585 new G4PVPlacement(G4Translate3D(dx, -dy, dz),
593 sprintf(name,
"BKLM.Layer%02dRightGapChimneyPhysical", layer);
594 new G4PVPlacement(G4Translate3D(dx, +dy, dz),
603 sprintf(name,
"BKLM.Layer%02dChimneyBox", layer);
606 chimneyHalfSize.x(), chimneyHalfSize.y(), chimneyHalfSize.z()
608 sprintf(name,
"BKLM.Layer%02dChimneyLogical", layer);
609 G4LogicalVolume* chimneyLogical =
610 new G4LogicalVolume(chimneyBox,
614 chimneyLogical->SetVisAttributes(m_VisAttributes.front());
616 G4Tubs* housingTube =
617 new G4Tubs(
"BKLM.ChimneyHousingTube",
618 m_GeoPar->getChimneyHousingInnerRadius() * CLHEP::cm,
619 m_GeoPar->getChimneyHousingOuterRadius() * CLHEP::cm,
624 G4LogicalVolume* housingLogical =
625 new G4LogicalVolume(housingTube,
627 "BKLM.ChimneyHousingLogical"
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),
634 "BKLM.ChimneyHousingPhysical",
641 new G4Tubs(
"BKLM.ChimneyShieldTube",
642 m_GeoPar->getChimneyShieldInnerRadius() * CLHEP::cm,
643 m_GeoPar->getChimneyShieldOuterRadius() * CLHEP::cm,
648 G4LogicalVolume* shieldLogical =
649 new G4LogicalVolume(shieldTube,
651 "BKLM.ChimneyShieldLogical"
653 shieldLogical->SetVisAttributes(m_VisAttributes.back());
654 new G4PVPlacement(G4RotateY3D(M_PI_2),
656 "BKLM.ChimneyShieldPhysical",
663 new G4Tubs(
"BKLM.ChimneyPipeTube",
664 m_GeoPar->getChimneyPipeInnerRadius() * CLHEP::cm,
665 m_GeoPar->getChimneyPipeOuterRadius() * CLHEP::cm,
670 G4LogicalVolume* pipeLogical =
671 new G4LogicalVolume(pipeTube,
673 "BKLM.ChimneyPipeLogical"
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),
680 "BKLM.ChimneyPipePhysical",
686 sprintf(name,
"BKLM.Layer%02dChimneyPhysical", layer);
687 new G4PVPlacement(G4Translate3D(chimneyPosition),
698 bool isFlipped,
int newLvol)
700 const CLHEP::Hep3Vector gapHalfSize = m_GeoPar->getGapHalfSize(layer, hasChimney) * CLHEP::cm;
701 const CLHEP::Hep3Vector moduleHalfSize = m_GeoPar->getModuleHalfSize(layer, hasChimney) * CLHEP::cm;
705 if (m_LayerModuleLogical[modLvol] ==
nullptr) {
707 sprintf(name,
"BKLM.Layer%02d%sModuleSolid", layer, (hasChimney ?
"Chimney" :
""));
710 moduleHalfSize.x(), moduleHalfSize.y(), moduleHalfSize.z()
712 m_LayerModuleLogical[modLvol] =
713 new G4LogicalVolume(moduleBox,
715 logicalName(moduleBox)
717 m_LayerModuleLogical[modLvol]->SetVisAttributes(m_VisAttributes.front());
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 =
722 interiorHalfSize1.x(), interiorHalfSize1.y(), interiorHalfSize1.z()
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 =
728 interiorHalfSize2.x(), interiorHalfSize2.y(), interiorHalfSize2.z()
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)
738 interiorLogical->SetVisAttributes(m_VisAttributes.front());
739 if (m_GeoPar->hasRPCs(layer)) {
740 putRPCsInInterior(interiorLogical, layer, hasChimney);
742 putScintsInInterior(interiorLogical, section, sector, layer, hasChimney);
744 new G4PVPlacement(G4TranslateZ3D(0.0),
746 physicalName(interiorLogical),
747 m_LayerModuleLogical[modLvol],
752 sprintf(name,
"BKLM.Layer%02d%sGapSolid", layer, (hasChimney ?
"Chimney" :
""));
753 m_LayerGapSolid[modLvol] =
755 gapHalfSize.x(), gapHalfSize.y(), gapHalfSize.z()
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],
765 m_LayerGapLogical[newLvol]->SetVisAttributes(m_VisAttributes.front());
767 double dx = (m_GeoPar->getModuleMiddleRadius(layer) - m_GeoPar->getGapMiddleRadius(layer)) * CLHEP::cm;
771 int s2 = m_GeoPar->getNSector() / 2;
774 }
else if (s1 > s2) {
777 double dz = moduleHalfSize.z() - gapHalfSize.z();
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],
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]),
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 =
806 electrodeHalfSize.x(), electrodeHalfSize.y(), electrodeHalfSize.z()
808 G4LogicalVolume* electrodeLogical =
809 new G4LogicalVolume(electrodeBox,
811 logicalName(electrodeBox)
813 electrodeLogical->SetVisAttributes(m_VisAttributes.front());
815 sprintf(name,
"BKLM.Layer%02d%sGasSolid", layer, (hasChimney ?
"Chimney" :
""));
816 const CLHEP::Hep3Vector gasHalfSize = m_GeoPar->getGasHalfSize(layer, hasChimney) * CLHEP::cm;
819 gasHalfSize.x(), gasHalfSize.y(), gasHalfSize.z()
821 G4LogicalVolume* gasLogical =
822 new G4LogicalVolume(gasBox,
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()),
834 physicalName(gasLogical),
841 new G4PVPlacement(G4TranslateX3D(+0.5 * electrodeHalfSize.x()),
843 physicalName(gasLogical),
849 new G4PVPlacement(G4TranslateZ3D(0.0),
851 physicalName(electrodeLogical),
862 sprintf(name,
"BKLM.Layer%02d%sAirSolid", layer, (hasChimney ?
"Chimney" :
""));
863 const CLHEP::Hep3Vector airHalfSize = m_GeoPar->getAirHalfSize(layer, hasChimney) * CLHEP::cm;
866 airHalfSize.x(), airHalfSize.y(), airHalfSize.z()
868 G4LogicalVolume* airLogical =
869 new G4LogicalVolume(airBox,
873 airLogical->SetVisAttributes(m_VisAttributes.front());
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 =
880 envelopeHalfSize.x(), envelopeHalfSize.y(), envelopeHalfSize.z()
882 G4LogicalVolume* innerEnvelopeLogical =
883 new G4LogicalVolume(scintEnvelopeBox,
885 logicalName(scintEnvelopeBox)
887 innerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front());
888 double scintHalfHeight = m_GeoPar->getScintHalfHeight() * CLHEP::cm;
889 double scintHalfWidth = m_GeoPar->getScintHalfWidth() * CLHEP::cm;
891 int envelopeOffsetSign = m_GeoPar->getScintEnvelopeOffsetSign(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),
900 physicalName(scintLogical),
901 innerEnvelopeLogical,
907 G4LogicalVolume* outerEnvelopeLogical =
908 new G4LogicalVolume(scintEnvelopeBox,
910 logicalName(scintEnvelopeBox)
912 outerEnvelopeLogical->SetVisAttributes(m_VisAttributes.front());
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),
921 physicalName(scintLogical),
922 outerEnvelopeLogical,
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),
938 new G4PVPlacement(G4Translate3D(+envelopeHalfSize.x(), envelopeOffset.y() * envelopeOffsetSign, envelopeOffset.z()),
939 outerEnvelopeLogical,
940 physicalName(outerEnvelopeLogical),
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;
953 if (m_GeoPar->getReadoutStationIsPhi(station)) {
954 xform = G4Translate3D(0.0, stationPosition, airHalfSize.z() - containerHalfSizeZ);
956 xform = G4Translate3D(0.0,
957 (containerHalfSizeZ - airHalfSize.y()) * envelopeOffsetSign,
958 airHalfSize.z() + stationPosition
959 ) * G4RotateX3D(M_PI_2 * envelopeOffsetSign);
961 if (fabs(xform.getTranslation().z()) > airHalfSize.z())
964 sprintf(name,
"BKLM.ReadoutContainer%dLayer%02d%sPhysical", station, layer, (hasChimney ?
"Chimney" :
""));
965 new G4PVPlacement(xform,
966 readoutContainerLogical,
976 new G4PVPlacement(G4TranslateX3D(m_GeoPar->getPolystyreneOffsetX() * CLHEP::cm),
978 physicalName(airLogical),
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; }
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());
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());
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());
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),
1031 physicalName(scintFiberLogical),
1037 new G4PVPlacement(G4TranslateZ3D(0.0),
1039 physicalName(scintBoreLogical),
1045 new G4PVPlacement(G4TranslateZ3D(-dzMPPC),
1047 physicalName(activeLogical),
1053 new G4PVPlacement(G4TranslateZ3D(0.0),
1054 getMPPCHousingLogical(),
1055 "BKLM.MPPCHousingPhysical",
1061 new G4PVPlacement(G4TranslateZ3D(dz),
1063 physicalName(scintAirLogical),
1069 return scintLogical;
1074 if (m_MPPCHousingLogical ==
nullptr) {
1075 G4Tubs* mppcHousingSolid =
1076 new G4Tubs(
"BKLM.MPPCHousingSolid",
1078 m_GeoPar->getMPPCHousingRadius() * CLHEP::cm,
1079 m_GeoPar->getMPPCHousingHalfLength() * CLHEP::cm,
1083 m_MPPCHousingLogical =
1084 new G4LogicalVolume(mppcHousingSolid,
1086 "BKLM>MPPCHousingLogical"
1088 m_VisAttributes.push_back(
new G4VisAttributes(
true));
1089 m_VisAttributes.back()->SetColour(0.5, 0.5, 0.5);
1090 m_MPPCHousingLogical->SetVisAttributes(m_VisAttributes.back());
1092 new G4Box(
"BKLM.MPPCSolid",
1093 m_GeoPar->getMPPCHalfLength() * CLHEP::cm,
1094 m_GeoPar->getMPPCHalfWidth() * CLHEP::cm,
1095 m_GeoPar->getMPPCHalfHeight() * CLHEP::cm
1097 G4LogicalVolume* mppcLogical =
1098 new G4LogicalVolume(mppcBox,
1102 m_VisAttributes.push_back(
new G4VisAttributes(
true));
1103 m_VisAttributes.back()->SetColour(1.0, 1.0, 1.0);
1104 mppcLogical->SetVisAttributes(m_VisAttributes.back());
1105 new G4PVPlacement(G4TranslateX3D(0.0),
1107 "BKLM.MPPCPhysical",
1108 m_MPPCHousingLogical,
1114 return m_MPPCHousingLogical;
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()
1125 m_ReadoutContainerLogical =
1126 new G4LogicalVolume(containerBox,
1128 logicalName(containerBox)
1130 m_ReadoutContainerLogical->SetVisAttributes(m_VisAttributes.front());
1132 const CLHEP::Hep3Vector carrierHalfSize = m_GeoPar->getReadoutCarrierHalfSize() * CLHEP::cm;
1134 new G4Box(
"BKLM.ReadoutCarrierSolid",
1135 carrierHalfSize.x(), carrierHalfSize.y(), carrierHalfSize.z()
1137 G4LogicalVolume* carrierLogical =
1138 new G4LogicalVolume(carrierBox,
1140 logicalName(carrierBox)
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()
1150 G4LogicalVolume* preamplifierLogical =
1151 new G4LogicalVolume(preamplifierBox,
1153 logicalName(preamplifierBox)
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()
1161 G4LogicalVolume* connectorsLogical =
1162 new G4LogicalVolume(connectorsBox,
1164 logicalName(connectorsBox)
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()),
1171 physicalName(carrierLogical),
1172 m_ReadoutContainerLogical,
1177 new G4PVPlacement(G4Translate3D(0.0, m_GeoPar->getReadoutConnectorsPosition(), -carrierHalfSize.z()),
1179 physicalName(connectorsLogical),
1180 m_ReadoutContainerLogical,
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,
1198 return m_ReadoutContainerLogical;
1204 if (m_SolenoidTube ==
nullptr) {
1206 new G4Tubs(
"BKLM.SolenoidTube",
1208 m_GeoPar->getSolenoidOuterRadius() * CLHEP::cm,
1209 2.0 * m_GeoPar->getHalfLength() * CLHEP::cm,
1214 return m_SolenoidTube;
1219 G4String* name =
new G4String(solid->GetName().substr(0, solid->GetName().size() - 5) +
"Logical");
1220 m_Names.push_back(name);
1226 G4String* name =
new G4String(lvol->GetName().substr(0, lvol->GetName().size() - 7) +
"Physical");
1227 m_Names.push_back(name);
@ c_ChimneySector
Chimney sector: BB3 in 1-based notation; BB2 in 0-based notation.
static constexpr int getMaximalLayerNumber()
Get maximal layer number (1-based).
@ c_ForwardSection
Forward.
@ c_BackwardSection
Backward.
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 ¶meters, 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.
Define the geometry of a BKLM module Each sector [octant] contains Modules.
static G4Material * get(const std::string &name)
Find given material.
double tan(double a)
tan for double
Common code concerning the geometry representation of the detector.
GeometryTypes
Flag indiciating the type of geometry to be used.