Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-13 07:51:24

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
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   // The blueprint to be created
0041   Acts::GeoModelBlueprintCreater::Blueprint blueprint;
0042 
0043   // The GeoModel tree must have a reader
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   // Prepare the map
0052   std::map<std::string, TableEntry> blueprintTableMap;
0053 
0054   Cache cache;
0055   // Prepare the KdtSurfaces if configured to do so
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     // Create the KdtSurfaces
0080     cache.kdtSurfaces = std::make_shared<Experimental::KdtSurfaces<3u>>(
0081         gctx, m_cfg.detectorSurfaces, kdtBinning);
0082   }
0083 
0084   // In order to guarantee the correct order
0085   // of the building sequence we need to build a representative tree first
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     // The bit more complicated strings from the database
0097     // volume bounds of top volume might be overruled for top node
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     // Split the bounds on the deliminater
0110     ACTS_DEBUG("Creating (" << volumeType << ") Blueprint node for volume "
0111                             << volumeName << " (id: " << volumeId << ")");
0112 
0113     // Create a table entry per defined volume
0114     TableEntry entry{volumeId,       volumeType,      volumeName,
0115                      volumeBounds,   volumeInternals, volumeBinnings,
0116                      volumeMaterials};
0117 
0118     // This will guarantee to have access to the building
0119     blueprintTableMap[volumeName] = entry;
0120   }
0121 
0122   // Now we can build the tree
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   // Recursively create the nodes
0131   blueprint.name = topEntry->second.name;
0132   blueprint.topNode =
0133       createNode(cache, gctx, topEntry->second, blueprintTableMap, Extent());
0134 
0135   // Export to dot graph if configured
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   // Return the ready-to-use blueprint
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   // Peak into the volume entry to understand which one should be constraint
0155   // by the internals building
0156   std::vector<AxisDirection> internalConstraints =
0157       detail::GeoModelExentHelper::readBoundsConstaints(entry.bounds, "i");
0158   // Check if the binnning will also use the internal constraints
0159   std::vector<AxisDirection> binningConstraints =
0160       detail::GeoModelExentHelper::readBinningConstraints(entry.binnings);
0161   // Concatenate the binning constraints
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   // Create and return the container node with internal constrtins
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   // Parse the bounds
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   // Check if material has to be attached
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         // Erase the p
0208         materialTokens[0u].erase(pPos, 1);
0209         // Get the portal number
0210         unsigned int portalNumber = std::stoi(materialTokens[0u]);
0211         // Get the binning description - first split the string
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   // Block for branch or container nodes that have children
0231   if (entryType == "branch" || entryType == "container" ||
0232       entryType == "root") {
0233     std::vector<std::unique_ptr<Experimental::Gen2Blueprint::Node>> children;
0234     // Check for gap filling
0235     bool gapFilling = false;
0236     // Check if the entry has children
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     // Create the sub nodes and keep track of the raw values
0245     for (const auto& childName : childrenNames) {
0246       std::string fChildName = entry.name + std::string("/") + childName;
0247       if (childName == "*") {
0248         // Gap volume detected
0249         gapFilling = true;
0250         ACTS_VERBOSE("Gap volume detected, gap filling will be triggered.");
0251         continue;
0252       }
0253       // Check for child and build it
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     // Create the binnings
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     // Complete the children
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       // Find the first child that is not a gap
0279       Experimental::detail::BlueprintHelper::fillGaps(*node, true);
0280     }
0281 
0282     // Attach a geoID generator if configured
0283     if (entryTypeSplit.size() > 1u) {
0284       // Get the geometry ID from the string
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       // Make the container geoID generator
0291       node->geoIdGenerator =
0292           std::make_shared<Experimental::GeometryIdGenerator>(
0293               geoIDCfg, m_logger->clone(entry.name + "_GeometryIdGenerator"));
0294     }
0295 
0296     // Create the branch node
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   // Check if the internals entry is empty
0320   if (entry.internals.empty()) {
0321     return {nullptr, Extent()};
0322   }
0323 
0324   // Build a layer structure
0325   if (entry.internals[0u] == "layer") {
0326     // Check if the internals entry is interpretable
0327     if (entry.internals.size() < 2u) {
0328       throw std::invalid_argument(
0329           "GeoModelBlueprintCreater: Internals entry not complete.");
0330     }
0331 
0332     // Internal split of the internals
0333     std::vector<std::string> internalsSplit;
0334     boost::split(internalsSplit, entry.internals[1u], boost::is_any_of(","));
0335 
0336     // Prepare an internal extent
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       // Fill what we have - follow the convention to fill up with the last
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       // Create the search range
0361       RangeXD<3u, double> searchRange{mins, maxs};
0362       auto surfaces = cache.kdtSurfaces->surfaces(searchRange);
0363       // Loop over surfaces and create an internal extent
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       // Create the layer structure builder
0374       Experimental::LayerStructureBuilder::Config lsbCfg;
0375       lsbCfg.surfacesProvider =
0376           std::make_shared<Experimental::LayerStructureBuilder::SurfacesHolder>(
0377               surfaces);
0378 
0379       // Let's check the binning description
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   // Create the return values
0417   Vector3 translation{0., 0., 0.};
0418   std::vector<double> boundValues = {};
0419   auto [boundsType, extent] = detail::GeoModelExentHelper::extentFromTable(
0420       boundsEntrySplit, externalExtent, internalExtent);
0421 
0422   // Switch on the bounds type
0423   if (boundsType == VolumeBounds::BoundsType::eCylinder) {
0424     // Create the translation & bound values
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 }