12 #include <top/geometry/GeoTOPCreator.h>
13 #include <top/geometry/TOPGeometryPar.h>
15 #include <geometry/Materials.h>
16 #include <geometry/CreatorFactory.h>
17 #include <geometry/utilities.h>
18 #include <framework/gearbox/GearDir.h>
19 #include <framework/gearbox/Unit.h>
20 #include <framework/logging/Logger.h>
21 #include <framework/database/Database.h>
22 #include <framework/database/IntervalOfValidity.h>
23 #include <framework/database/DBImportObjPtr.h>
24 #include <framework/database/DBObjPtr.h>
25 #include <top/simulation/SensitivePMT.h>
26 #include <top/simulation/SensitiveBar.h>
27 #include <simulation/background/BkgSensitiveDetector.h>
28 #include <simulation/kernel/RunManager.h>
32 #include <G4LogicalVolume.hh>
33 #include <G4PVPlacement.hh>
34 #include <G4AssemblyVolume.hh>
35 #include <G4LogicalSkinSurface.hh>
36 #include <G4OpticalSurface.hh>
39 #include <G4UserLimits.hh>
40 #include <G4Material.hh>
41 #include <G4ExtrudedSolid.hh>
42 #include <G4UnionSolid.hh>
43 #include <G4Sphere.hh>
44 #include <G4IntersectionSolid.hh>
45 #include <G4SubtractionSolid.hh>
46 #include <G4Region.hh>
47 #include <G4Colour.hh>
48 #include <G4TwoVector.hh>
49 #include <G4ThreeVector.hh>
61 using namespace geometry;
66 geometry::CreatorFactory<GeoTOPCreator> GeoTOPFactory(
"TOPCreator");
69 GeoTOPCreator::GeoTOPCreator()
73 GeoTOPCreator::~GeoTOPCreator()
75 if (m_sensitivePMT)
delete m_sensitivePMT;
76 if (m_sensitiveBar)
delete m_sensitiveBar;
77 if (m_sensitivePCB1)
delete m_sensitivePCB1;
78 if (m_sensitivePCB2)
delete m_sensitivePCB2;
79 G4LogicalSkinSurface::CleanSurfaceTable();
83 void GeoTOPCreator::create(
const GearDir& content, G4LogicalVolume& topVolume,
87 m_isBeamBkgStudy = content.getInt(
"BeamBackgroundStudy");
89 m_topgp->Initialize(content);
90 if (!m_topgp->isValid()) {
91 B2ERROR(
"TOP: geometry or mappers not valid (gearbox) - geometry not created");
95 const auto* geo = m_topgp->getGeometry();
96 createGeometry(*geo, topVolume, type);
101 void GeoTOPCreator::createPayloads(
const GearDir& content,
104 m_topgp->Initialize(content);
105 if (!m_topgp->isValid()) {
106 B2ERROR(
"TOP: geometry or mappers not valid (gearbox) - no payloads imported");
111 const auto* geo = m_topgp->getGeometry();
115 m_topgp->getChannelMapper().import(iov);
116 m_topgp->getFrontEndMapper().import(iov);
118 B2RESULT(
"TOP: geometry and mappers imported to database");
123 void GeoTOPCreator::createFromDB(
const std::string& name, G4LogicalVolume& topVolume,
127 m_topgp->Initialize();
128 if (!m_topgp->isValid()) {
129 B2ERROR(
"Cannot create Geometry from Database: no configuration found for "
134 const auto* geo = m_topgp->getGeometry();
135 createGeometry(*geo, topVolume, type);
141 G4LogicalVolume& topVolume,
147 m_sensitivePMT->setModuleReplicaDepth(4);
148 m_sensitiveBar->setReplicaDepth(1);
155 G4Region* aRegion =
new G4Region(
"TOPEnvelope");
157 for (
const auto& geoModule : geo.
getModules()) {
158 int moduleID = geoModule.getModuleID();
159 double barLength = geoModule.getBarLength();
164 G4TranslateZ3D(backwardLength / 2 - prismPosition - barLength / 2);
168 const auto& displacement = geoModule.getModuleDisplacement();
169 G4Transform3D dRx = G4RotateX3D(displacement.getAlpha());
170 G4Transform3D dRy = G4RotateY3D(displacement.getBeta());
171 G4Transform3D dRz = G4RotateZ3D(displacement.getGamma());
172 G4Transform3D dtr = G4Translate3D(displacement.getX(),
174 displacement.getZ());
175 G4Transform3D D = dtr * dRz * dRy * dRx;
179 double radius = geoModule.getRadius();
180 double backwardZ = geoModule.getBackwardZ();
181 G4Transform3D tr = G4Translate3D(0, radius, barLength / 2 + backwardZ);
182 double phi = geoModule.getPhi() - M_PI / 2;
183 G4Transform3D Rz = G4RotateZ3D(phi);
184 G4Transform3D T = Rz * tr * D * T1;
185 auto* module = createModule(geo, moduleID);
186 std::string name = geoModule.getName();
189 module->SetRegion(aRegion);
190 aRegion->AddRootLogicalVolume(module);
192 new G4PVPlacement(T, module, name, &topVolume,
false, moduleID);
195 if (m_numDecoupledPMTs > 0) B2WARNING(
"GeoTOPCreator, " << m_numDecoupledPMTs
196 <<
" PMT's are optically decoupled");
197 if (m_numBrokenGlues > 0) B2WARNING(
"GeoTOPCreator, " << m_numBrokenGlues
199 if (m_numPeelOffRegions > 0) B2WARNING(
"GeoTOPCreator, " << m_numPeelOffRegions
200 <<
" peel-off cookie regions");
204 G4LogicalVolume* GeoTOPCreator::createModule(
const TOPGeometry& geo,
int moduleID)
209 G4RotationMatrix rot;
213 const auto& geoQBB = geo.
getQBB();
214 auto* module = createModuleEnvelope(geoQBB, moduleID);
218 const auto& geoModule = geo.
getModule(moduleID);
219 auto* optics = assembleOptics(geoModule);
221 move.setZ(geoQBB.getPrismPosition() - geoQBB.getPrismEnclosure().getLength() / 2);
222 optics->MakeImprint(module, move, &rot);
229 double Lprism = geoModule.getPrism().getFullLength();
230 double Larray = geoModule.getPMTArray().getSizeZ();
231 move.setZ(move.z() - Lprism - Larray);
232 m_frontEnd->MakeImprint(module, move, &rot);
236 if (!m_qbb) m_qbb = assembleQBB(geoQBB);
238 m_qbb->MakeImprint(module, move, &rot);
244 G4LogicalVolume* GeoTOPCreator::createModuleEnvelope(
const TOPGeoQBB& geo,
249 if (!m_moduleEnvelope) {
251 double forwardLength = geo.
getLength() - backwardLength;
252 std::vector<G4TwoVector> polygon;
254 polygon.push_back(G4TwoVector(point.first, point.second));
256 auto* backward =
new G4ExtrudedSolid(
"backwardEnvelope",
257 polygon, backwardLength / 2,
258 G4TwoVector(), 1, G4TwoVector(), 1);
261 polygon.push_back(G4TwoVector(point.first, point.second));
263 auto* forward =
new G4ExtrudedSolid(
"forwardEnvelope",
264 polygon, forwardLength / 2,
265 G4TwoVector(), 1, G4TwoVector(), 1);
267 G4Transform3D move = G4TranslateZ3D((backwardLength + forwardLength) / 2);
268 m_moduleEnvelope =
new G4UnionSolid(
"moduleEnvelope", backward, forward, move);
271 G4Material* material = Materials::get(geo.
getMaterial());
272 if (!material) B2FATAL(
"Material '" << geo.
getMaterial() <<
"' not found");
274 std::string name = addNumber(
"TOPEnvelopeModule", moduleID);
275 return new G4LogicalVolume(m_moduleEnvelope, material, name);
280 G4AssemblyVolume* GeoTOPCreator::assembleFrontEnd(
const TOPGeoFrontEnd& geo,
int N)
282 auto* frontEnd =
new G4AssemblyVolume();
283 Simulation::RunManager::Instance().addAssemblyVolume(frontEnd);
291 auto* frontBoard = createBox(
"TOPFrontBoard",
296 if (m_isBeamBkgStudy) {
298 frontBoard->SetSensitiveDetector(m_sensitivePCB1);
300 move.setZ(Z - length / 2);
302 frontEnd->AddPlacedVolume(frontBoard, move, 0);
308 auto* HVBoard = createBox(
"TOPHVBoard",
313 if (m_isBeamBkgStudy) {
315 HVBoard->SetSensitiveDetector(m_sensitivePCB2);
319 frontEnd->AddPlacedVolume(HVBoard, move, 0);
326 auto* boardStack = createBoardStack(geo, N);
327 frontEnd->AddPlacedVolume(boardStack, move, 0);
333 G4LogicalVolume* GeoTOPCreator::createBoardStack(
const TOPGeoFrontEnd& geo,
int N)
336 double fullWidth = width * N;
337 std::string name =
"TOPBoardStack";
338 auto* boardStack = createBox(name,
345 std::string name1 = name +
"Spacer";
346 auto* spacer = createBox(name1,
352 std::string name2 = name +
"TwoSpacers";
353 auto* twoSpacers = createBox(name2,
360 move = G4TranslateX3D(-(fullWidth - spacerWidth) / 2);
361 new G4PVPlacement(move, spacer, name1, boardStack,
false, 1);
362 move = G4TranslateX3D((fullWidth - spacerWidth) / 2);
363 new G4PVPlacement(move, spacer, name1, boardStack,
false, 2);
366 for (
int i = 0; i < n; i++) {
367 move = G4TranslateX3D(width * (2 * i - n + 1) / 2.0);
368 new G4PVPlacement(move, twoSpacers, name2, boardStack,
false, i + 1);
375 G4AssemblyVolume* GeoTOPCreator::assembleQBB(
const TOPGeoQBB& geo)
377 auto* qbb =
new G4AssemblyVolume();
378 Simulation::RunManager::Instance().addAssemblyVolume(qbb);
387 auto* outerPanel = createHoneycombPanel(geo, c_Outer);
389 qbb->AddPlacedVolume(outerPanel, move, 0);
393 auto* innerPanel = createHoneycombPanel(geo, c_Inner);
395 qbb->AddPlacedVolume(innerPanel, move, 0);
405 move.setZ(Zfront + length / 2);
406 qbb->AddPlacedVolume(forwardEndPlate, move, 0);
413 auto* backPlate = createExtrudedSolid(name +
"BackPlate",
417 move.setZ(Z + length / 2);
418 qbb->AddPlacedVolume(backPlate, move, 0);
422 auto* prismEnclosure = createExtrudedSolid(name +
"Body",
426 move.setZ(Z + length / 2);
427 qbb->AddPlacedVolume(prismEnclosure, move, 0);
431 auto* frontPlate = createExtrudedSolid(name +
"FrontPlate",
435 move.setZ(Z + length / 2);
436 qbb->AddPlacedVolume(frontPlate, move, 0);
442 auto* extPlate = createBox(name +
"ExtensionPlate",
448 G4ThreeVector movePlate;
449 movePlate.setZ(Z + length / 2);
451 qbb->AddPlacedVolume(extPlate, movePlate, 0);
456 auto* leftRail = createSideRail(geo, c_Left);
461 qbb->AddPlacedVolume(leftRail, move, 0);
463 auto* rightRail = createSideRail(geo, c_Right);
465 qbb->AddPlacedVolume(rightRail, move, 0);
480 qbb->AddPlacedVolume(coldPlateBase, move, 0);
489 qbb->AddPlacedVolume(coldPlateCool, move, 0);
495 G4LogicalVolume* GeoTOPCreator::createHoneycombPanel(
const TOPGeoQBB& geo,
502 double sideEdgeHeight = 0;
503 double sideEdgeY = 0;
505 if (type == c_Inner) {
509 sideEdgeY = geoPanel.
getY() - sideEdgeHeight / 2;
514 sideEdgeY = geoPanel.
getY() + sideEdgeHeight / 2;
519 auto* panel = createExtrudedSolid(geoPanel.
getName(),
526 std::string faceEdgeName = geoPanel.
getName() +
"ReinforcedFace";
527 auto* faceEdge = createExtrudedSolid(faceEdgeName,
533 new G4PVPlacement(move, faceEdge, faceEdgeName, panel,
false, 1);
535 new G4PVPlacement(move, faceEdge, faceEdgeName, panel,
false, 2);
541 std::string sideEdgeName = geoPanel.
getName() +
"ReinforcedSide";
542 auto* sideEdge = createBox(sideEdgeName,
549 move = G4Translate3D((width - sideEdgeWidth) / 2, sideEdgeY, 0);
550 new G4PVPlacement(move, sideEdge, sideEdgeName, panel,
false, 1);
551 move = G4Translate3D(-(width - sideEdgeWidth) / 2, sideEdgeY, 0);
552 new G4PVPlacement(move, sideEdge, sideEdgeName, panel,
false, 2);
558 G4LogicalVolume* GeoTOPCreator::createSideRail(
const TOPGeoQBB& geo,
569 auto* box =
new G4Box(
"box", A / 2, B / 2, C / 2);
570 auto* subtrBox =
new G4Box(
"subtrBox", a / 2, b / 2, c / 2);
572 if (type == c_Left) {
577 G4Transform3D move = G4Translate3D(x, -(B - b) / 2, -(C - c) / 2);
578 auto* solid =
new G4SubtractionSolid(
"sideRail", box, subtrBox, move);
585 if (type == c_Left) {
591 return new G4LogicalVolume(solid, material, name);
595 G4AssemblyVolume* GeoTOPCreator::assembleOptics(
const TOPGeoModule& geo)
597 auto* optics =
new G4AssemblyVolume();
598 Simulation::RunManager::Instance().addAssemblyVolume(optics);
613 G4RotationMatrix rotArray;
615 optics->AddPlacedVolume(pmtArray, moveArray, &rotArray);
618 G4RotationMatrix rot;
622 rot.rotateY(M_PI / 2);
623 optics->AddPlacedVolume(prism, move, &rot);
627 optics->AddPlacedVolume(barSegment2, move, 0);
630 move.setZ(L2 + L1 / 2);
631 optics->AddPlacedVolume(barSegment1, move, 0);
635 move.setZ(L2 + L1 + Lm / 2);
636 optics->AddPlacedVolume(mirrorSegment, move, 0);
641 const double thickness = 1.0;
646 auto* peekFrameAbove = createBox(
"PeekFrameAbove", width, thickness, length,
"Al");
647 move.setZ(-(Lp - length / 2));
648 move.setY(Yup + thickness / 2);
649 optics->AddPlacedVolume(peekFrameAbove, move, 0);
651 auto* peekFrameBelow = createBox(
"PeekFrameBelow", width, thickness, length,
"Al");
652 move.setZ(-(Lp - length / 2));
653 move.setY(Ydn - thickness / 2);
654 optics->AddPlacedVolume(peekFrameBelow, move, 0);
666 auto* bar = createBox(geo.
getName(),
670 auto* glue = createBox(geo.
getName() +
"Glue",
674 auto* brokenGlue = createExtrudedSolid(geo.
getName() +
"BrokenGlue",
679 new G4PVPlacement(fix, brokenGlue, geo.
getName() +
"BrokenGlue", glue,
false, 1);
680 B2RESULT(
"GeoTOPCreator, broken glue at " << geo.
getName()
681 << addNumber(
" of Slot", moduleID));
687 new G4PVPlacement(move, glue, geo.
getName() +
"Glue", bar,
false, 1);
690 auto& materials = Materials::getInstance();
691 auto* optSurf = materials.createOpticalSurface(geo.
getSurface());
693 new G4LogicalSkinSurface(
"opticalSurface", bar, optSurf);
696 bar->SetSensitiveDetector(m_sensitiveBar);
708 auto* box =
new G4Box(geo.
getName(),
713 auto* bar = createBoxSphereIntersection(geo.
getName(),
720 auto* glue = createBox(geo.
getName() +
"Glue",
724 auto* brokenGlue = createExtrudedSolid(geo.
getName() +
"BrokenGlue",
729 new G4PVPlacement(fix, brokenGlue, geo.
getName() +
"BrokenGlue", glue,
false, 1);
730 B2RESULT(
"GeoTOPCreator, broken glue at " << geo.
getName()
731 << addNumber(
" of Slot", moduleID));
737 new G4PVPlacement(move, glue, geo.
getName() +
"Glue", bar,
false, 1);
740 auto& materials = Materials::getInstance();
741 auto* optSurf = materials.createOpticalSurface(geo.
getSurface());
743 new G4LogicalSkinSurface(
"opticalSurface", bar, optSurf);
746 auto* mirror = createBoxSphereIntersection(geo.
getName() +
"ReflectiveCoating",
754 new G4LogicalSkinSurface(
"mirrorSurface", mirror, mirrorSurf);
757 move = G4TranslateZ3D(0);
758 new G4PVPlacement(move, mirror, geo.
getName() +
"ReflectiveCoating", bar,
false, 1);
761 bar->SetSensitiveDetector(m_sensitiveBar);
767 G4LogicalVolume* GeoTOPCreator::createPrism(
const TOPGeoPrism& geo,
int moduleID)
774 std::vector<G4TwoVector> polygon;
775 polygon.push_back(G4TwoVector(0, geo.
getThickness() / 2));
781 polygon.push_back(G4TwoVector(0, -geo.
getThickness() / 2));
783 auto* volume =
new G4ExtrudedSolid(geo.
getName(), polygon, geo.
getWidth() / 2,
784 G4TwoVector(), 1, G4TwoVector(), 1);
785 G4Material* material = Materials::get(geo.
getMaterial());
786 if (!material) B2FATAL(
"Material '" << geo.
getMaterial() <<
"' not found");
787 auto* prism =
new G4LogicalVolume(volume, material, geo.
getName());
794 std::string filterMaterial;
795 if (filterThickness > 0) {
808 std::string message = addNumber(
"peel-off cookie regions of Slot", moduleID) +
":";
810 if (region.fraction <= 0)
continue;
811 std::string name = addNumber(geo.
getName() +
"PeelOff", region.ID);
813 auto* peelOff = createExtrudedSolid(name,
820 G4Transform3D R = G4RotateY3D(-M_PI / 2);
821 G4Transform3D moveRegion = T * R;
822 new G4PVPlacement(moveRegion, peelOff, name,
filter,
false, 1);
823 message += addNumber(
" ", region.ID);
826 if (numRegions > 0) B2RESULT(
"GeoTOPCreator, " << message);
827 m_numPeelOffRegions += numRegions;
833 new G4PVPlacement(move,
filter, geo.
getName() +
"Filter", prism,
false, 1);
838 auto& materials = Materials::getInstance();
839 auto* optSurf = materials.createOpticalSurface(geo.
getSurface());
841 new G4LogicalSkinSurface(
"opticalSurface", prism, optSurf);
845 prism->SetSensitiveDetector(m_sensitiveBar);
855 auto* pmtArray = createBox(geo.
getName(),
861 auto* pmt = createPMT(geo.
getPMT());
864 std::string message = addNumber(
"optically decoupled PMT's of Slot", moduleID) +
":";
865 for (
unsigned row = 1; row <= geo.
getNumRows(); row++) {
871 message += addNumber(
" ",
id);
872 m_numDecoupledPMTs++;
875 G4Transform3D move = G4Translate3D(geo.
getX(col), geo.
getY(row), z);
876 new G4PVPlacement(move, pmt, addNumber(geo.
getPMT().
getName(),
id), pmtArray,
889 auto* cookie = createBox(geo.
getName() +
"Cookie",
893 G4Transform3D move = G4Translate3D(0, 0, (fullThickness - cookieThickness) / 2);
894 new G4PVPlacement(move, cookie, geo.
getName() +
"Cookie",
filter,
false, 1);
896 move = G4Translate3D(0, 0, (geo.
getSizeZ() - fullThickness) / 2);
897 new G4PVPlacement(move,
filter, geo.
getName() +
"Filter+Cookie", pmtArray,
901 if (!geo.
getDecoupledPMTs().empty()) B2RESULT(
"GeoTOPCreator, " << message);
907 G4LogicalVolume* GeoTOPCreator::createPMT(
const TOPGeoPMT& geo)
912 auto* pmt = createBox(geo.
getName(),
918 auto* window = createBox(geo.
getName() +
"Window",
922 auto* photoCathode = createBox(geo.
getName() +
"PhotoCathode",
926 photoCathode->SetSensitiveDetector(m_sensitivePMT);
930 new G4PVPlacement(move, photoCathode, geo.
getName() +
"PhotoCathode", window,
933 auto* reflEdge = createBox(geo.
getName() +
"ReflectiveEdge",
939 if (holeSizeX > 0 and holeSizeY > 0) {
940 auto* hole = createBox(geo.
getName() +
"ReflectiveEdgeHole",
941 holeSizeX, holeSizeY,
944 move = G4TranslateZ3D(0.0);
945 new G4PVPlacement(move, hole, geo.
getName() +
"ReflectiveEdgeHole", reflEdge,
949 auto& materials = Materials::getInstance();
951 new G4LogicalSkinSurface(
"reflectiveEdgeSurface", reflEdge, optSurf);
954 new G4PVPlacement(move, reflEdge, geo.
getName() +
"ReflectiveEdge", window,
957 move = G4TranslateZ3D((geo.
getSizeZ() - winThickness) / 2);
958 new G4PVPlacement(move, window, geo.
getName() +
"Window", pmt,
false, 1);
962 auto* bottom = createBox(geo.
getName() +
"Bottom",
966 new G4PVPlacement(move, bottom, geo.
getName() +
"Bottom", pmt,
false, 1);
971 auto* interior = createBox(geo.
getName() +
"Interior",
976 move = G4TranslateZ3D(-(geo.
getSizeZ() - interiorSizeZ) / 2
978 new G4PVPlacement(move, interior, geo.
getName() +
"Interior", pmt,
false, 1);
984 G4LogicalVolume* GeoTOPCreator::createBox(
const std::string& name,
985 double A,
double B,
double C,
986 const std::string& materialName)
988 G4Box* box =
new G4Box(name, A / 2, B / 2, C / 2);
989 G4Material* material = Materials::get(materialName);
990 if (!material) B2FATAL(
"Material '" << materialName <<
"' not found");
991 return new G4LogicalVolume(box, material, name);
996 GeoTOPCreator::createBoxSphereIntersection(
const std::string& name,
1003 const std::string& materialName)
1006 double x = box->GetXHalfLength();
1007 double y = box->GetYHalfLength();
1008 double z = box->GetZHalfLength();
1009 double dx = fmax(fabs(-x - xc), fabs(x - xc));
1010 double dy = fmax(fabs(-y - yc), fabs(y - yc));
1011 double dz = fmin(-z - zc, z - zc);
1012 double theta = atan(sqrt(dx * dx + dy * dy) / dz);
1014 auto* sphere =
new G4Sphere(name +
"Sphere",
1015 Rmin, Rmax, 0, 2 * M_PI, 0, theta);
1016 G4Translate3D move(xc, yc, zc);
1017 auto* shape =
new G4IntersectionSolid(name, box, sphere, move);
1019 G4Material* material = Materials::get(materialName);
1020 if (!material) B2FATAL(
"Material '" << materialName <<
"' not found");
1021 return new G4LogicalVolume(shape, material, name);
1025 G4LogicalVolume* GeoTOPCreator::createExtrudedSolid(
const std::string& name,
1026 const Polygon& shape,
1028 const std::string& materialName)
1030 std::vector<G4TwoVector> polygon;
1031 for (
auto& point : shape) {
1032 polygon.push_back(G4TwoVector(point.first, point.second));
1034 G4ExtrudedSolid* solid =
new G4ExtrudedSolid(name, polygon, length / 2,
1035 G4TwoVector(), 1, G4TwoVector(), 1);
1036 G4Material* material = Materials::get(materialName);
1037 if (!material) B2FATAL(
"Material '" << materialName <<
"' not found");
1038 return new G4LogicalVolume(solid, material, name);
1042 std::string GeoTOPCreator::addNumber(
const std::string& str,
unsigned number)
1046 ss <<
"0" << number;