File indexing completed on 2025-10-15 08:05:03
0001
0002
0003
0004
0005
0006
0007
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
0042 GeoModelBlueprintCreater::Blueprint blueprint;
0043
0044
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
0053 std::map<std::string, TableEntry> blueprintTableMap;
0054
0055 Cache cache;
0056
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
0081 cache.kdtSurfaces = std::make_shared<Experimental::KdtSurfaces<3u>>(
0082 gctx, m_cfg.detectorSurfaces, kdtBinning);
0083 }
0084
0085
0086
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
0098
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
0111 ACTS_DEBUG("Creating (" << volumeType << ") Blueprint node for volume "
0112 << volumeName << " (id: " << volumeId << ")");
0113
0114
0115 TableEntry entry{volumeId, volumeType, volumeName,
0116 volumeBounds, volumeInternals, volumeBinnings,
0117 volumeMaterials};
0118
0119
0120 blueprintTableMap[volumeName] = entry;
0121 }
0122
0123
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
0132 blueprint.name = topEntry->second.name;
0133 blueprint.topNode =
0134 createNode(cache, gctx, topEntry->second, blueprintTableMap, Extent());
0135
0136
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
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
0156
0157 std::vector<AxisDirection> internalConstraints =
0158 detail::GeoModelExentHelper::readBoundsConstaints(entry.bounds, "i");
0159
0160 std::vector<AxisDirection> binningConstraints =
0161 detail::GeoModelExentHelper::readBinningConstraints(entry.binnings);
0162
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
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
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
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
0209 materialTokens[0u].erase(pPos, 1);
0210
0211 unsigned int portalNumber = std::stoi(materialTokens[0u]);
0212
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
0232 if (entryType == "branch" || entryType == "container" ||
0233 entryType == "root") {
0234 std::vector<std::unique_ptr<Experimental::Gen2Blueprint::Node>> children;
0235
0236 bool gapFilling = false;
0237
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
0246 for (const auto& childName : childrenNames) {
0247 std::string fChildName = entry.name + std::string("/") + childName;
0248 if (childName == "*") {
0249
0250 gapFilling = true;
0251 ACTS_VERBOSE("Gap volume detected, gap filling will be triggered.");
0252 continue;
0253 }
0254
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
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
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
0280 Experimental::detail::BlueprintHelper::fillGaps(*node, true);
0281 }
0282
0283
0284 if (entryTypeSplit.size() > 1u) {
0285
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
0292 node->geoIdGenerator =
0293 std::make_shared<Experimental::GeometryIdGenerator>(
0294 geoIDCfg, m_logger->clone(entry.name + "_GeometryIdGenerator"));
0295 }
0296
0297
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
0321 if (entry.internals.empty()) {
0322 return {nullptr, Extent()};
0323 }
0324
0325
0326 if (entry.internals[0u] == "layer") {
0327
0328 if (entry.internals.size() < 2u) {
0329 throw std::invalid_argument(
0330 "GeoModelBlueprintCreater: Internals entry not complete.");
0331 }
0332
0333
0334 std::vector<std::string> internalsSplit;
0335 boost::split(internalsSplit, entry.internals[1u], boost::is_any_of(","));
0336
0337
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
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
0362 RangeXD<3u, double> searchRange{mins, maxs};
0363 auto surfaces = cache.kdtSurfaces->surfaces(searchRange);
0364
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
0375 Experimental::LayerStructureBuilder::Config lsbCfg;
0376 lsbCfg.surfacesProvider =
0377 std::make_shared<Experimental::LayerStructureBuilder::SurfacesHolder>(
0378 surfaces);
0379
0380
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
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 = 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 }