File indexing completed on 2025-07-13 07:51:24
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Plugins/GeoModel/GeoModelBlueprintCreater.hpp"
0010
0011 #include "Acts/Detector/GeometryIdGenerator.hpp"
0012 #include "Acts/Detector/LayerStructureBuilder.hpp"
0013 #include "Acts/Detector/detail/BlueprintDrawer.hpp"
0014 #include "Acts/Detector/detail/BlueprintHelper.hpp"
0015 #include "Acts/Detector/interface/IGeometryIdGenerator.hpp"
0016 #include "Acts/Plugins/GeoModel/GeoModelTree.hpp"
0017 #include "Acts/Plugins/GeoModel/detail/GeoModelBinningHelper.hpp"
0018 #include "Acts/Plugins/GeoModel/detail/GeoModelExtentHelper.hpp"
0019 #include "Acts/Utilities/BinningType.hpp"
0020 #include "Acts/Utilities/Enumerate.hpp"
0021 #include "Acts/Utilities/Helpers.hpp"
0022 #include "Acts/Utilities/RangeXD.hpp"
0023
0024 #include <algorithm>
0025 #include <fstream>
0026 #include <ranges>
0027
0028 #include <boost/algorithm/string.hpp>
0029
0030 using namespace Acts::detail;
0031
0032 Acts::GeoModelBlueprintCreater::GeoModelBlueprintCreater(
0033 const Config& cfg, std::unique_ptr<const Logger> mlogger)
0034 : m_cfg(cfg), m_logger(std::move(mlogger)) {}
0035
0036 Acts::GeoModelBlueprintCreater::Blueprint
0037 Acts::GeoModelBlueprintCreater::create(const GeometryContext& gctx,
0038 const GeoModelTree& gmTree,
0039 const Options& options) const {
0040
0041 Acts::GeoModelBlueprintCreater::Blueprint blueprint;
0042
0043
0044 if (gmTree.dbMgr == nullptr) {
0045 throw std::invalid_argument(
0046 "GeoModelBlueprintCreater: GeoModelTree has no dbMgr");
0047 }
0048
0049 auto blueprintTable = gmTree.dbMgr->getTableRecords_String(options.table);
0050
0051
0052 std::map<std::string, TableEntry> blueprintTableMap;
0053
0054 Cache cache;
0055
0056
0057 if (!m_cfg.detectorSurfaces.empty()) {
0058 std::array<AxisDirection, 3u> kdtBinning = {
0059 AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0060 if (m_cfg.kdtBinning.empty()) {
0061 throw std::invalid_argument(
0062 "GeoModelBlueprintCreater: At least one binning value for KDTree "
0063 "structure has to be given");
0064 } else if (m_cfg.kdtBinning.size() == 1u) {
0065 kdtBinning = {m_cfg.kdtBinning[0u], m_cfg.kdtBinning[0u],
0066 m_cfg.kdtBinning[0u]};
0067 } else if (m_cfg.kdtBinning.size() == 2u) {
0068 kdtBinning = {m_cfg.kdtBinning[0u], m_cfg.kdtBinning[1u],
0069 m_cfg.kdtBinning[1u]};
0070 } else if (m_cfg.kdtBinning.size() == 3u) {
0071 kdtBinning = {m_cfg.kdtBinning[0u], m_cfg.kdtBinning[1u],
0072 m_cfg.kdtBinning[2u]};
0073 } else {
0074 throw std::invalid_argument(
0075 "GeoModelBlueprintCreater: Too many binning values for KDTree "
0076 "structure");
0077 }
0078
0079
0080 cache.kdtSurfaces = std::make_shared<Experimental::KdtSurfaces<3u>>(
0081 gctx, m_cfg.detectorSurfaces, kdtBinning);
0082 }
0083
0084
0085
0086 for (const auto& line : blueprintTable) {
0087 if (line.size() != 7u) {
0088 throw std::invalid_argument(
0089 "GeoModelBlueprintCreater: Blueprint table has wrong number of "
0090 "columns");
0091 }
0092
0093 int volumeId = std::stoi(line.at(0));
0094 std::string volumeType = line.at(1);
0095 std::string volumeName = line.at(2);
0096
0097
0098 std::string volumeBounds =
0099 (!options.topBoundsOverride.empty() && volumeName == options.topEntry)
0100 ? options.topBoundsOverride
0101 : line.at(3);
0102 std::vector<std::string> volumeInternals;
0103 boost::split(volumeInternals, line.at(4), boost::is_any_of(":"));
0104 std::vector<std::string> volumeBinnings;
0105 boost::split(volumeBinnings, line.at(5), boost::is_any_of(";"));
0106 std::vector<std::string> volumeMaterials;
0107 boost::split(volumeMaterials, line.at(6), boost::is_any_of("|"));
0108
0109
0110 ACTS_DEBUG("Creating (" << volumeType << ") Blueprint node for volume "
0111 << volumeName << " (id: " << volumeId << ")");
0112
0113
0114 TableEntry entry{volumeId, volumeType, volumeName,
0115 volumeBounds, volumeInternals, volumeBinnings,
0116 volumeMaterials};
0117
0118
0119 blueprintTableMap[volumeName] = entry;
0120 }
0121
0122
0123 auto topEntry = blueprintTableMap.find(options.topEntry);
0124 if (topEntry == blueprintTableMap.end()) {
0125 throw std::invalid_argument("GeoModelBlueprintCreater: Top node '" +
0126 options.topEntry +
0127 "' not found in blueprint table");
0128 }
0129
0130
0131 blueprint.name = topEntry->second.name;
0132 blueprint.topNode =
0133 createNode(cache, gctx, topEntry->second, blueprintTableMap, Extent());
0134
0135
0136 if (!options.dotGraph.empty()) {
0137 std::ofstream dotFile(options.dotGraph);
0138 Experimental::detail::BlueprintDrawer::dotStream(dotFile,
0139 *blueprint.topNode);
0140 dotFile.close();
0141 }
0142
0143
0144 return blueprint;
0145 }
0146
0147 std::unique_ptr<Acts::Experimental::Gen2Blueprint::Node>
0148 Acts::GeoModelBlueprintCreater::createNode(
0149 Cache& cache, const GeometryContext& gctx, const TableEntry& entry,
0150 const std::map<std::string, TableEntry>& tableEntryMap,
0151 const Extent& motherExtent) const {
0152 ACTS_DEBUG("Build Blueprint node for '" << entry.name << "'.");
0153
0154
0155
0156 std::vector<AxisDirection> internalConstraints =
0157 detail::GeoModelExentHelper::readBoundsConstaints(entry.bounds, "i");
0158
0159 std::vector<AxisDirection> binningConstraints =
0160 detail::GeoModelExentHelper::readBinningConstraints(entry.binnings);
0161
0162 for (const auto& bc : binningConstraints) {
0163 if (!rangeContainsValue(internalConstraints, bc)) {
0164 internalConstraints.push_back(bc);
0165 }
0166 }
0167
0168 if (!internalConstraints.empty()) {
0169 ACTS_VERBOSE("Found " << internalConstraints.size()
0170 << " internal constraints to check for: ");
0171 for (const auto& ic : internalConstraints) {
0172 ACTS_VERBOSE("- " << axisDirectionName(ic));
0173 }
0174 }
0175
0176
0177 auto [internalsBuilder, internalExtent] = createInternalStructureBuilder(
0178 cache, gctx, entry, motherExtent, internalConstraints);
0179
0180 if (internalsBuilder != nullptr) {
0181 ACTS_VERBOSE("Internal building yielded extent "
0182 << internalExtent.toString());
0183 }
0184
0185
0186 auto [boundsType, extent, boundValues, translation] =
0187 parseBounds(entry.bounds, motherExtent, internalExtent);
0188
0189 ACTS_VERBOSE("Creating with extent " << extent.toString());
0190
0191 Transform3 transform = Acts::Transform3::Identity();
0192 transform.translation() = translation;
0193
0194 std::vector<std::string> entryTypeSplit;
0195 boost::split(entryTypeSplit, entry.type, boost::is_any_of(":"));
0196 std::string entryType = entryTypeSplit[0u];
0197
0198
0199 std::map<unsigned int, std::vector<DirectedProtoAxis>> portalMaterialBinning;
0200 if (!entry.materials.empty()) {
0201 for (const auto& material : entry.materials) {
0202 std::vector<std::string> materialTokens;
0203 boost::split(materialTokens, material, boost::is_any_of(":"));
0204 ACTS_DEBUG(" - Material detected for " << materialTokens[0u]);
0205 auto pPos = materialTokens[0u].find("p");
0206 if (pPos != std::string::npos) {
0207
0208 materialTokens[0u].erase(pPos, 1);
0209
0210 unsigned int portalNumber = std::stoi(materialTokens[0u]);
0211
0212 std::vector<std::string> binningTokens;
0213 boost::split(binningTokens, materialTokens[1u], boost::is_any_of(";"));
0214
0215 std::vector<DirectedProtoAxis> protoBinnings;
0216 for (const auto& bToken : binningTokens) {
0217 ACTS_VERBOSE(" - Binning: " << bToken);
0218 auto [dpAxis, nB] =
0219 detail::GeoModelBinningHelper::toProtoAxis(bToken, extent);
0220 protoBinnings.push_back(dpAxis);
0221 }
0222 portalMaterialBinning[portalNumber] = protoBinnings;
0223 }
0224 }
0225 ACTS_VERBOSE("Node " << entry.name << " has "
0226 << portalMaterialBinning.size()
0227 << " material portals.");
0228 }
0229
0230
0231 if (entryType == "branch" || entryType == "container" ||
0232 entryType == "root") {
0233 std::vector<std::unique_ptr<Experimental::Gen2Blueprint::Node>> children;
0234
0235 bool gapFilling = false;
0236
0237 if (entry.internals.size() < 2u) {
0238 throw std::invalid_argument(
0239 "GeoModelBlueprintCreater: Branch node '" + entry.name +
0240 "' has no children defined in blueprint table");
0241 }
0242 std::vector<std::string> childrenNames;
0243 boost::split(childrenNames, entry.internals[1u], boost::is_any_of(","));
0244
0245 for (const auto& childName : childrenNames) {
0246 std::string fChildName = entry.name + std::string("/") + childName;
0247 if (childName == "*") {
0248
0249 gapFilling = true;
0250 ACTS_VERBOSE("Gap volume detected, gap filling will be triggered.");
0251 continue;
0252 }
0253
0254 auto childEntry = tableEntryMap.find(fChildName);
0255 if (childEntry == tableEntryMap.end()) {
0256 throw std::invalid_argument("GeoModelBlueprintCreater: Child node '" +
0257 childName + "' of '" + entry.name +
0258 "' NOT found in blueprint table");
0259 }
0260 auto node =
0261 createNode(cache, gctx, childEntry->second, tableEntryMap, extent);
0262 children.push_back(std::move(node));
0263 }
0264
0265
0266 std::vector<Acts::AxisDirection> binnings;
0267 std::ranges::for_each(entry.binnings, [&binnings](const std::string& b) {
0268 binnings.push_back(detail::GeoModelBinningHelper::toAxisDirection(b));
0269 });
0270
0271
0272 auto node = std::make_unique<Experimental::Gen2Blueprint::Node>(
0273 entry.name, transform, boundsType, boundValues, binnings,
0274 std::move(children), extent);
0275 node->portalMaterialBinning = portalMaterialBinning;
0276
0277 if (gapFilling) {
0278
0279 Experimental::detail::BlueprintHelper::fillGaps(*node, true);
0280 }
0281
0282
0283 if (entryTypeSplit.size() > 1u) {
0284
0285 int geoID = std::stoi(entryTypeSplit[1u]);
0286 Experimental::GeometryIdGenerator::Config geoIDCfg;
0287 geoIDCfg.containerMode = true;
0288 geoIDCfg.containerId = geoID;
0289 geoIDCfg.resetSubCounters = true;
0290
0291 node->geoIdGenerator =
0292 std::make_shared<Experimental::GeometryIdGenerator>(
0293 geoIDCfg, m_logger->clone(entry.name + "_GeometryIdGenerator"));
0294 }
0295
0296
0297 return node;
0298
0299 } else if (entryType == "leaf") {
0300 auto node = std::make_unique<Experimental::Gen2Blueprint::Node>(
0301 entry.name, transform, boundsType, boundValues, internalsBuilder,
0302 extent);
0303 node->portalMaterialBinning = portalMaterialBinning;
0304 return node;
0305 } else {
0306 throw std::invalid_argument(
0307 "GeoModelBlueprintCreater: Unknown node type '" + entry.type + "'");
0308 }
0309
0310 return nullptr;
0311 }
0312
0313 std::tuple<std::shared_ptr<const Acts::Experimental::IInternalStructureBuilder>,
0314 Acts::Extent>
0315 Acts::GeoModelBlueprintCreater::createInternalStructureBuilder(
0316 Cache& cache, const GeometryContext& gctx, const TableEntry& entry,
0317 const Extent& externalExtent,
0318 const std::vector<AxisDirection>& internalConstraints) const {
0319
0320 if (entry.internals.empty()) {
0321 return {nullptr, Extent()};
0322 }
0323
0324
0325 if (entry.internals[0u] == "layer") {
0326
0327 if (entry.internals.size() < 2u) {
0328 throw std::invalid_argument(
0329 "GeoModelBlueprintCreater: Internals entry not complete.");
0330 }
0331
0332
0333 std::vector<std::string> internalsSplit;
0334 boost::split(internalsSplit, entry.internals[1u], boost::is_any_of(","));
0335
0336
0337 Extent internalExtent;
0338 if (internalsSplit[0u] == "kdt" && cache.kdtSurfaces != nullptr) {
0339 std::vector<std::string> internalsData = {internalsSplit.begin() + 1,
0340 internalsSplit.end()};
0341 auto [boundsType, rangeExtent] =
0342 detail::GeoModelExentHelper::extentFromTable(internalsData,
0343 externalExtent);
0344
0345 ACTS_VERBOSE("Requested range: " << rangeExtent.toString());
0346 std::array<double, 3u> mins = {};
0347 std::array<double, 3u> maxs = {};
0348
0349
0350 for (std::size_t ibv = 0; ibv < 3u; ++ibv) {
0351 if (ibv < m_cfg.kdtBinning.size()) {
0352 AxisDirection v = m_cfg.kdtBinning[ibv];
0353 mins[ibv] = rangeExtent.min(v);
0354 maxs[ibv] = rangeExtent.max(v);
0355 continue;
0356 }
0357 mins[ibv] = rangeExtent.min(m_cfg.kdtBinning.back());
0358 maxs[ibv] = rangeExtent.max(m_cfg.kdtBinning.back());
0359 }
0360
0361 RangeXD<3u, double> searchRange{mins, maxs};
0362 auto surfaces = cache.kdtSurfaces->surfaces(searchRange);
0363
0364 for (auto& sf : surfaces) {
0365 auto sfExtent =
0366 sf->polyhedronRepresentation(gctx, m_cfg.quarterSegments).extent();
0367 internalExtent.extend(sfExtent, internalConstraints);
0368 }
0369 ACTS_VERBOSE("Found " << surfaces.size() << " surfaces in range "
0370 << searchRange.toString());
0371 ACTS_VERBOSE("Internal extent: " << internalExtent.toString());
0372
0373
0374 Experimental::LayerStructureBuilder::Config lsbCfg;
0375 lsbCfg.surfacesProvider =
0376 std::make_shared<Experimental::LayerStructureBuilder::SurfacesHolder>(
0377 surfaces);
0378
0379
0380 if (!entry.binnings.empty()) {
0381 ACTS_VERBOSE("Binning description detected for this layer structure.");
0382 for (const auto& binning : entry.binnings) {
0383 if (!binning.empty()) {
0384 ACTS_VERBOSE("- Adding binning: " << binning);
0385 lsbCfg.binnings.push_back(
0386 detail::GeoModelBinningHelper::toProtoAxis(binning,
0387 internalExtent));
0388 }
0389 }
0390 } else {
0391 lsbCfg.nMinimalSurfaces = surfaces.size() + 1u;
0392 }
0393
0394 return {
0395 std::make_shared<Experimental::LayerStructureBuilder>(
0396 lsbCfg, m_logger->clone(entry.name + "_LayerStructureBuilder")),
0397 internalExtent};
0398
0399 } else {
0400 throw std::invalid_argument(
0401 "GeoModelBlueprintCreater: Unknown layer internals type '" +
0402 entry.internals[1u] + "' / or now kdt surfaces provided.");
0403 }
0404 }
0405 return {nullptr, Extent()};
0406 }
0407
0408 std::tuple<Acts::VolumeBounds::BoundsType, Acts::Extent, std::vector<double>,
0409 Acts::Vector3>
0410 Acts::GeoModelBlueprintCreater::parseBounds(
0411 const std::string& boundsEntry, const Extent& externalExtent,
0412 const Extent& internalExtent) const {
0413 std::vector<std::string> boundsEntrySplit;
0414 boost::split(boundsEntrySplit, boundsEntry, boost::is_any_of(","));
0415
0416
0417 Vector3 translation{0., 0., 0.};
0418 std::vector<double> boundValues = {};
0419 auto [boundsType, extent] = detail::GeoModelExentHelper::extentFromTable(
0420 boundsEntrySplit, externalExtent, internalExtent);
0421
0422
0423 if (boundsType == VolumeBounds::BoundsType::eCylinder) {
0424
0425 translation = Acts::Vector3(0., 0., extent.medium(AxisDirection::AxisZ));
0426 boundValues = {extent.min(AxisDirection::AxisR),
0427 extent.max(AxisDirection::AxisR),
0428 0.5 * extent.interval(AxisDirection::AxisZ)};
0429 } else {
0430 throw std::invalid_argument(
0431 "GeoModelBlueprintCreater: Unknown bounds type, only 'cyl' is "
0432 "supported for the moment.");
0433 }
0434
0435 return {boundsType, extent, boundValues, translation};
0436 }