Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 09:35:01

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