File indexing completed on 2025-02-22 09:35:01
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 <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
0039 Acts::GeoModelBlueprintCreater::Blueprint blueprint;
0040
0041
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
0051 std::map<std::string, TableEntry> blueprintTableMap;
0052
0053 Cache cache;
0054
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
0079 cache.kdtSurfaces = std::make_shared<Experimental::KdtSurfaces<3u>>(
0080 gctx, m_cfg.detectorSurfaces, kdtBinning);
0081 }
0082
0083
0084
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
0096
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
0109 ACTS_DEBUG("Creating (" << volumeType << ") Blueprint node for volume "
0110 << volumeName << " (id: " << volumeId << ")");
0111
0112
0113 TableEntry entry{volumeId, volumeType, volumeName,
0114 volumeBounds, volumeInternals, volumeBinnings,
0115 volumeMaterials};
0116
0117
0118 blueprintTableMap[volumeName] = entry;
0119 }
0120
0121
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
0130 blueprint.name = topEntry->second.name;
0131 blueprint.topNode =
0132 createNode(cache, gctx, topEntry->second, blueprintTableMap, Extent());
0133
0134
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
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
0154
0155 std::vector<AxisDirection> internalConstraints =
0156 detail::GeoModelExentHelper::readBoundsConstaints(entry.bounds, "i");
0157
0158 std::vector<AxisDirection> binningConstraints =
0159 detail::GeoModelExentHelper::readBinningConstraints(entry.binnings);
0160
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
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
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
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
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<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
0231 if (entryType == "branch" || entryType == "container" ||
0232 entryType == "root") {
0233 std::vector<std::unique_ptr<Experimental::Blueprint::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::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
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
0281 Experimental::detail::BlueprintHelper::fillGaps(*node, true);
0282 }
0283
0284
0285 if (entryTypeSplit.size() > 1u) {
0286
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
0293 node->geoIdGenerator =
0294 std::make_shared<Experimental::GeometryIdGenerator>(
0295 geoIDCfg, m_logger->clone(entry.name + "_GeometryIdGenerator"));
0296 }
0297
0298
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
0322 if (entry.internals.empty()) {
0323 return {nullptr, Extent()};
0324 }
0325
0326
0327 if (entry.internals[0u] == "layer") {
0328
0329 if (entry.internals.size() < 2u) {
0330 throw std::invalid_argument(
0331 "GeoModelBlueprintCreater: Internals entry not complete.");
0332 }
0333
0334
0335 std::vector<std::string> internalsSplit;
0336 boost::split(internalsSplit, entry.internals[1u], boost::is_any_of(","));
0337
0338
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
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
0363 RangeXD<3u, double> searchRange{mins, maxs};
0364 auto surfaces = cache.kdtSurfaces->surfaces(searchRange);
0365
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
0376 Experimental::LayerStructureBuilder::Config lsbCfg;
0377 lsbCfg.surfacesProvider =
0378 std::make_shared<Experimental::LayerStructureBuilder::SurfacesHolder>(
0379 surfaces);
0380
0381
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
0419 Vector3 translation{0., 0., 0.};
0420 std::vector<double> boundValues = {};
0421 auto [boundsType, extent] = detail::GeoModelExentHelper::extentFromTable(
0422 boundsEntrySplit, externalExtent, internalExtent);
0423
0424
0425 if (boundsType == VolumeBounds::BoundsType::eCylinder) {
0426
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 }