File indexing completed on 2025-10-28 07:55:49
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Detector/LayerStructureBuilder.hpp"
0012 #include "Acts/Geometry/GeometryContext.hpp"
0013 #include "Acts/Utilities/BinningType.hpp"
0014 #include "Acts/Utilities/StringHelpers.hpp"
0015 #include "ActsPlugins/Geant4/Geant4SurfaceProvider.hpp"
0016 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0017
0018 #include <filesystem>
0019 #include <memory>
0020 #include <string>
0021
0022 #include "G4Box.hh"
0023 #include "G4GDMLParser.hh"
0024 #include "G4LogicalVolume.hh"
0025 #include "G4NistManager.hh"
0026 #include "G4PVPlacement.hh"
0027 #include "G4RotationMatrix.hh"
0028 #include "G4SystemOfUnits.hh"
0029 #include "G4ThreeVector.hh"
0030 #include "G4Transform3D.hh"
0031
0032 using namespace Acts;
0033 using namespace Acts::Experimental;
0034 using namespace ActsPlugins;
0035
0036
0037
0038
0039 EAxis binToGeant4Axis(const AxisDirection& bv) {
0040 switch (bv) {
0041 case AxisDirection::AxisX:
0042 return EAxis::kXAxis;
0043 case AxisDirection::AxisY:
0044 return EAxis::kYAxis;
0045 case AxisDirection::AxisZ:
0046 return EAxis::kZAxis;
0047 case AxisDirection::AxisR:
0048 return EAxis::kRho;
0049 case AxisDirection::AxisPhi:
0050 return EAxis::kPhi;
0051 default:
0052 throw std::invalid_argument(
0053 "No Geant4 axis conversion for this binning value");
0054 }
0055 }
0056
0057 const int nLayers = 5;
0058 const int nChips = 5;
0059 const int nArms = 2;
0060
0061 const double cellDimX = 0.4 * cm;
0062 const double cellDimY = 0.3 * cm;
0063 const double cellDimZ = 0.1 * cm;
0064
0065 const double armOffset = 10 * cm;
0066
0067 const std::filesystem::path gdmlPath = "two-arms-telescope.gdml";
0068
0069 namespace ActsTests {
0070
0071 BOOST_AUTO_TEST_SUITE(Geant4Suite)
0072
0073 std::tuple<G4VPhysicalVolume*, std::vector<std::string>>
0074 ConstructGeant4World() {
0075
0076 G4NistManager* nist = G4NistManager::Instance();
0077
0078
0079
0080 G4bool checkOverlaps = true;
0081
0082
0083
0084
0085 G4double worldSizeXY = 50 * cm;
0086 G4double worldSizeZ = 50 * cm;
0087 G4Material* worldMat = nist->FindOrBuildMaterial("G4_Galactic");
0088
0089 auto solidWorld = new G4Box("World", 0.5 * worldSizeXY, 0.5 * worldSizeXY,
0090 0.5 * worldSizeZ);
0091
0092 auto logicWorld = new G4LogicalVolume(solidWorld, worldMat, "World");
0093
0094 auto physWorld = new G4PVPlacement(nullptr, G4ThreeVector(), logicWorld,
0095 "World", nullptr, false, 0, checkOverlaps);
0096
0097 G4Material* material = nist->FindOrBuildMaterial("G4_He");
0098
0099 std::vector<std::string> names;
0100 for (int nArm = 0; nArm < nArms; nArm++) {
0101 for (int nLayer = 0; nLayer < nLayers; nLayer++) {
0102 for (int nChip = 0; nChip < nChips; nChip++) {
0103 int sign = (nArm == 0) ? 1 : -1;
0104 double posX = sign * (armOffset + nChip * cm);
0105 double posY = 0;
0106 double posZ = nLayer * cm;
0107 G4ThreeVector pos = G4ThreeVector(posX, posY, posZ);
0108 G4ThreeVector dims = G4ThreeVector(cellDimX, cellDimY, cellDimZ);
0109
0110 int cellId = nChips * nLayers * nArm + nChips * nLayer + nChip;
0111 std::string name = "cell" + std::to_string(cellId);
0112
0113 names.push_back(name);
0114
0115
0116 auto solidCell = new G4Box(name, dims[0], dims[1], dims[2]);
0117
0118 G4LogicalVolume* cellLogical =
0119 new G4LogicalVolume(solidCell, material, "cellSensitive");
0120
0121 new G4PVPlacement(nullptr, pos, cellLogical, name, logicWorld, false, 0,
0122 true);
0123 }
0124 }
0125 }
0126
0127
0128 G4GDMLParser parser;
0129 parser.SetOutputFileOverwrite(true);
0130 parser.Write(gdmlPath.string(), physWorld);
0131
0132 return {physWorld, names};
0133 }
0134
0135 auto gctx = GeometryContext();
0136
0137
0138 auto [physWorld, names] = ConstructGeant4World();
0139
0140 BOOST_AUTO_TEST_CASE(Geant4SurfaceProviderNames) {
0141
0142 G4GDMLParser parser;
0143 parser.Read(gdmlPath.string(), false);
0144 auto world = parser.GetWorldVolume();
0145
0146
0147
0148 auto spFullCfg = Geant4SurfaceProvider<>::Config();
0149 spFullCfg.g4World = world;
0150 spFullCfg.surfacePreselector =
0151 std::make_shared<Geant4PhysicalVolumeSelectors::NameSelector>(names,
0152 true);
0153
0154 auto spFull = std::make_shared<Geant4SurfaceProvider<>>(
0155 spFullCfg, Geant4SurfaceProvider<>::kdtOptions());
0156
0157 auto lbFullCfg = LayerStructureBuilder::Config();
0158 lbFullCfg.surfacesProvider = spFull;
0159
0160 auto lbFull = std::make_shared<LayerStructureBuilder>(lbFullCfg);
0161
0162 auto [sFull, vFull, suFull, vuFull] = lbFull->construct(gctx);
0163
0164 BOOST_CHECK_EQUAL(sFull.size(), names.size());
0165 for (int nArm = 0; nArm < nArms; nArm++) {
0166 for (int nLayer = 0; nLayer < nLayers; nLayer++) {
0167 for (int nChip = 0; nChip < nChips; nChip++) {
0168 int sign = (nArm == 0) ? 1 : -1;
0169 double posX = sign * (armOffset + nChip * cm);
0170 double posY = 0;
0171 double posZ = nLayer * cm;
0172 Vector3 pos = Vector3(posX, posY, posZ);
0173
0174 BOOST_CHECK_EQUAL(
0175 sFull.at(nChips * nLayers * nArm + nChips * nLayer + nChip)
0176 ->center(gctx),
0177 pos);
0178 }
0179 }
0180 }
0181
0182
0183
0184 std::vector<std::string> leftArmNames(names.begin(),
0185 names.begin() + names.size() / 2);
0186
0187 auto spLeftArmCfg = Geant4SurfaceProvider<>::Config();
0188 spLeftArmCfg.g4World = world;
0189 spLeftArmCfg.surfacePreselector =
0190 std::make_shared<Geant4PhysicalVolumeSelectors::NameSelector>(
0191 leftArmNames, true);
0192
0193 auto spLeftArm = std::make_shared<Geant4SurfaceProvider<>>(
0194 spLeftArmCfg, Geant4SurfaceProvider<>::kdtOptions());
0195
0196 auto lbCfg = LayerStructureBuilder::Config();
0197 lbCfg.surfacesProvider = spLeftArm;
0198
0199 auto lbLeftArm = std::make_shared<LayerStructureBuilder>(lbCfg);
0200
0201 auto [sLeftArm, vLeftArm, suLeftArm, vuLeftArm] = lbLeftArm->construct(gctx);
0202
0203 BOOST_CHECK_EQUAL(sLeftArm.size(), leftArmNames.size());
0204 for (int nLayer = 0; nLayer < nLayers; nLayer++) {
0205 for (int nChip = 0; nChip < nChips; nChip++) {
0206 double posX = armOffset + nChip * cm;
0207 double posY = 0;
0208 double posZ = nLayer * cm;
0209 Vector3 pos = Vector3(posX, posY, posZ);
0210
0211 BOOST_CHECK_EQUAL(sLeftArm.at(nChips * nLayer + nChip)->center(gctx),
0212 pos);
0213 }
0214 }
0215 }
0216
0217 BOOST_AUTO_TEST_CASE(Geant4SurfaceProviderRanges) {
0218
0219 G4GDMLParser parser;
0220 parser.Read(gdmlPath.string(), false);
0221 auto world = parser.GetWorldVolume();
0222
0223
0224 auto sp1DCfg = Geant4SurfaceProvider<1>::Config();
0225 sp1DCfg.g4World = world;
0226
0227 auto kdt1DOpt = Geant4SurfaceProvider<1>::kdtOptions();
0228 kdt1DOpt.range = RangeXD<1, double>();
0229 kdt1DOpt.range[0].set(8, 12);
0230 kdt1DOpt.binningValues = {AxisDirection::AxisZ};
0231
0232 auto sp1D = std::make_shared<Geant4SurfaceProvider<1>>(sp1DCfg, kdt1DOpt);
0233
0234 auto lb1DCfg = LayerStructureBuilder::Config();
0235 lb1DCfg.surfacesProvider = sp1D;
0236
0237 auto lb1D = std::make_shared<LayerStructureBuilder>(lb1DCfg);
0238
0239 auto [s1D, v1D, su1D, vu1D] = lb1D->construct(gctx);
0240
0241 BOOST_CHECK_EQUAL(s1D.size(), nChips * nArms);
0242 for (int nArm = 0; nArm < nArms; nArm++) {
0243 for (int nChip = 0; nChip < nChips; nChip++) {
0244 int sign = (nArm == 0) ? 1 : -1;
0245 double posX = sign * (armOffset + nChip * cm);
0246 double posY = 0;
0247 double posZ = 10;
0248 Vector3 pos = Vector3(posX, posY, posZ);
0249
0250 BOOST_CHECK_EQUAL(s1D.at(nChips * nArm + nChip)->center(gctx), pos);
0251 }
0252 }
0253
0254
0255
0256 auto sp2DCfg = Geant4SurfaceProvider<2>::Config();
0257 sp2DCfg.g4World = world;
0258
0259 auto kdt2DOpt = Geant4SurfaceProvider<2>::kdtOptions();
0260 kdt2DOpt.range = RangeXD<2, double>();
0261 kdt2DOpt.range[0].set(8, 12);
0262 kdt2DOpt.range[1].set(armOffset - 5, armOffset + 100);
0263 kdt2DOpt.binningValues = {AxisDirection::AxisZ};
0264
0265 auto sp2D = std::make_shared<Geant4SurfaceProvider<2>>(sp2DCfg, kdt2DOpt);
0266
0267 auto lb2DCfg = LayerStructureBuilder::Config();
0268 lb2DCfg.surfacesProvider = sp2D;
0269
0270 auto lb2D = std::make_shared<LayerStructureBuilder>(lb2DCfg);
0271
0272 auto [s2D, v2D, su2D, vu2D] = lb2D->construct(gctx);
0273
0274 BOOST_CHECK_EQUAL(s2D.size(), nChips);
0275 for (int nChip = 0; nChip < nChips; nChip++) {
0276 double posX = armOffset + nChip * cm;
0277 double posY = 0, posZ = 10;
0278 Vector3 pos = Vector3(posX, posY, posZ);
0279
0280 BOOST_CHECK_EQUAL(s2D.at(nChip)->center(gctx), pos);
0281 }
0282
0283
0284
0285 auto sp2DPosCfg = Geant4SurfaceProvider<1>::Config();
0286 sp2DPosCfg.g4World = world;
0287 std::map<unsigned int, std::tuple<double, double>> ranges;
0288
0289 std::array<unsigned int, 3> g4Axes{0};
0290 for (auto& bv :
0291 {AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ}) {
0292 g4Axes[toUnderlying(bv)] = binToGeant4Axis(bv);
0293 }
0294
0295 ranges[g4Axes[0]] = std::make_tuple(armOffset - 5, armOffset + 100);
0296 ranges[g4Axes[1]] = std::make_tuple(-100, 100);
0297 ranges[g4Axes[2]] = std::make_tuple(-100, 100);
0298
0299 sp2DPosCfg.surfacePreselector =
0300 std::make_shared<Geant4PhysicalVolumeSelectors::PositionSelector>(ranges);
0301
0302 auto sp2DPos =
0303 std::make_shared<Geant4SurfaceProvider<1>>(sp2DPosCfg, kdt1DOpt);
0304
0305 auto lb2DPosCfg = LayerStructureBuilder::Config();
0306 lb2DPosCfg.surfacesProvider = sp2DPos;
0307
0308 auto lb2DPos = std::make_shared<LayerStructureBuilder>(lb2DPosCfg);
0309
0310 auto [s2DPos, v2DPos, su2DPos, vu2DPos] = lb2DPos->construct(gctx);
0311
0312 BOOST_CHECK_EQUAL(s2DPos.size(), nChips);
0313 for (int nChip = 0; nChip < nChips; nChip++) {
0314 double posX = armOffset + nChip * cm;
0315 double posY = 0;
0316 double posZ = 10;
0317 Vector3 pos = Vector3(posX, posY, posZ);
0318
0319 BOOST_CHECK_EQUAL(s2DPos.at(nChip)->center(gctx), pos);
0320 }
0321 }
0322
0323 const char* gdml_head_xml =
0324 R"""(<?xml version="1.0" ?>
0325 <gdml xmlns:xsi="http:
0326
0327 BOOST_AUTO_TEST_CASE(Geant4RectangleFromGDML) {
0328 std::ofstream bgdml;
0329 bgdml.open("Plane.gdml");
0330 bgdml << gdml_head_xml;
0331 bgdml << "<solids>" << '\n';
0332 bgdml << "<box name=\"wb\" x=\"100\" y=\"100\" z=\"500\" lunit=\"mm\"/>"
0333 << '\n';
0334 bgdml << "<box name=\"c\" x=\"35\" y=\"55\" z=\"90\" lunit=\"mm\"/>" << '\n';
0335 bgdml << "<box name=\"b\" x=\"25\" y=\"50\" z=\"1\" lunit=\"mm\"/>" << '\n';
0336 bgdml << "</solids>" << '\n';
0337 bgdml << "<structure>" << '\n';
0338 bgdml << " <volume name=\"b\">" << '\n';
0339 bgdml << " <materialref ref=\"G4_Fe\"/>" << '\n';
0340 bgdml << " <solidref ref=\"b\"/>" << '\n';
0341 bgdml << " </volume>" << '\n';
0342 bgdml << " <volume name=\"cl\">" << '\n';
0343 bgdml << " <materialref ref=\"G4_Galactic\"/>" << '\n';
0344 bgdml << " <solidref ref=\"c\"/>" << '\n';
0345 bgdml << " <physvol name=\"b_pv\">" << '\n';
0346 bgdml << " <volumeref ref=\"b\"/>" << '\n';
0347 bgdml << " <position name=\"b_pv_pos\" unit=\"mm\" "
0348 "x=\"0\" y=\"5.\" z=\"0\"/>"
0349 << '\n';
0350 bgdml << " <rotation name=\"b_pv_rot\" unit=\"deg\" "
0351 "x=\"-90\" y=\"0\" z=\"0\"/>"
0352 << '\n';
0353 bgdml << " </physvol>" << '\n';
0354 bgdml << " </volume>" << '\n';
0355 bgdml << " <volume name=\"wl\">" << '\n';
0356 bgdml << " <materialref ref=\"G4_Galactic\"/>" << '\n';
0357 bgdml << " <solidref ref=\"wb\"/>" << '\n';
0358 bgdml << " <physvol name=\"cl_pv\">" << '\n';
0359 bgdml << " <volumeref ref=\"cl\"/>" << '\n';
0360 bgdml << " <rotation name=\"cl_pv_rot\" unit=\"deg\" "
0361 "x=\"-90\" y=\"0\" z=\"0\"/>"
0362 << '\n';
0363 bgdml << " </physvol>" << '\n';
0364 bgdml << " </volume>" << '\n';
0365 bgdml << "</structure>" << '\n';
0366 bgdml << "<setup name=\"Default\" version=\"1.0\">" << '\n';
0367 bgdml << " <world ref=\"wl\"/>" << '\n';
0368 bgdml << "</setup>" << '\n';
0369 bgdml << "</gdml>" << '\n';
0370
0371 bgdml.close();
0372
0373
0374 G4GDMLParser parser;
0375 parser.Read("Plane.gdml", false);
0376 auto world = parser.GetWorldVolume();
0377
0378
0379 auto planeFromGDMLCfg = Geant4SurfaceProvider<1>::Config();
0380 planeFromGDMLCfg.g4World = world;
0381 planeFromGDMLCfg.surfacePreselector =
0382 std::make_shared<Geant4PhysicalVolumeSelectors::NameSelector>(
0383 std::vector<std::string>{"b_pv"}, true);
0384
0385 auto kdt1DOpt = Geant4SurfaceProvider<1>::kdtOptions();
0386 kdt1DOpt.range = RangeXD<1, double>();
0387 kdt1DOpt.range[0].set(-100, 100);
0388 kdt1DOpt.binningValues = {AxisDirection::AxisZ};
0389
0390 auto tContext = GeometryContext();
0391
0392 auto planeProvider =
0393 std::make_shared<Geant4SurfaceProvider<1>>(planeFromGDMLCfg, kdt1DOpt);
0394
0395 auto planes = planeProvider->surfaces(tContext);
0396 BOOST_CHECK_EQUAL(planes.size(), 1u);
0397 CHECK_CLOSE_ABS(planes.front()->center(tContext).z(), 5., 1e-6);
0398 }
0399
0400 BOOST_AUTO_TEST_SUITE_END()
0401
0402 }