Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-15 08:05:03

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