Skip to content

Commit

Permalink
feat: dd4hep surface factory (#2501)
Browse files Browse the repository at this point in the history
This is the next step in preparing the ODD chain for old and new
detector.
It adds the new DD4hep surface factory and first set of unit tests.

@dimitra97 since you start to have DD4hep experience, I requested your
review.
  • Loading branch information
asalzburger committed Oct 5, 2023
1 parent 691f1d5 commit a3013a0
Show file tree
Hide file tree
Showing 20 changed files with 1,689 additions and 22 deletions.
3 changes: 2 additions & 1 deletion Plugins/DD4hep/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ include(FetchContent)
add_library(
ActsPluginDD4hep SHARED
src/ConvertDD4hepDetector.cpp
src/ConvertDD4hepMaterial.cpp
src/DD4hepMaterialHelpers.cpp
src/DD4hepDetectorElement.cpp
src/DD4hepDetectorSurfaceFactory.cpp
src/DD4hepLayerBuilder.cpp
src/DD4hepVolumeBuilder.cpp)
target_include_directories(
Expand Down
166 changes: 166 additions & 0 deletions Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepBinningHelpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/Definitions/Units.hpp"
#include "Acts/Detector/ProtoBinning.hpp"
#include "Acts/Plugins/DD4hep/DD4hepConversionHelpers.hpp"
#include "Acts/Utilities/BinningData.hpp"
#include "Acts/Utilities/detail/AxisFwd.hpp"

#include <string>
#include <tuple>
#include <vector>

#include <DD4hep/DD4hepUnits.h>
#include <DD4hep/DetElement.h>
#include <DD4hep/DetFactoryHelper.h>
#include <DD4hep/Objects.h>
#include <DDRec/DetectorData.h>
#include <XML/Utilities.h>

namespace Acts {

static std::vector<std::tuple<std::string, BinningValue>> allowedBinnings = {
{"x", binX}, {"y", binY}, {"z", binZ}, {"phi", binPhi}, {"r", binR}};

/// Helper method to decode the binning from what would appear in the
/// xml into variant parameters, such that it can be understood in the
/// downstream processing.
///
/// This parses the dediced \< surface_binning \> tag
/// - allowed/understood binnings are x,y,z,phi,r
/// - allowed/unserstood types are equidistant/variable (those are
/// auto-detected)
///
/// Example for e.g. bname = \"surface_binning\":
///
/// - Equidistant binning in r and phi:
/// \< surface_binning nr=\"2\" rmin=\"25\" rmax=\"100\" nphi=\"22\"
/// phimin=\"-3.1415\" phimax=\"3.1415\" \/ \>
/// - Variable binning in z:
/// \< surface_binning zboundaries=\"-100,-90,90,100\" \/ \>
///
/// And 2D combinations of this are allowed.
///
/// @param variantParams [in,out] the variant parameters that will be overwritten
/// @param xmlBinning the surface binning
/// @param bname the binning base name, e.g. surface_binning, material_binning
/// @param bvals the boundary values, i.e. x,y,z,phi,r
///
void decodeBinning(dd4hep::rec::VariantParameters &variantParams,
const xml_comp_t &xmlBinning, const std::string &bname,
const std::vector<std::string> &bvals) {
// Set the surface binninng parameter to true
variantParams.set<int>(std::string(bname + "_dim"), bvals.size());
for (const auto &bv : bvals) {
// Gather the number of bins, 0 indicates variable binning
int nBins = Acts::getAttrValueOr<int>(xmlBinning, std::string("n" + bv), 0);
// Gather the bin expansion parameter, expansion of 0 is default
int nExpansion =
Acts::getAttrValueOr<int>(xmlBinning, std::string(bv + "expansion"), 0);
variantParams.set<int>(bname + "_" + bv + "_exp", nExpansion);
// Equidistant binning detected
if (nBins > 0) {
// Set the type identificatio
variantParams.set<std::string>(bname + "_" + bv + "_type", "equidistant");
// Set the number of bins
variantParams.set<int>(bname + "_" + bv + "_n", nBins);
// Set min/max paraeter
variantParams.set<double>(
bname + "_" + bv + "_min",
xmlBinning.attr<double>(std::string(bv + "min").c_str()));
variantParams.set<double>(
bname + "_" + bv + "_max",
xmlBinning.attr<double>(std::string(bv + "max").c_str()));
} else {
// Variable binning detected
variantParams.set<std::string>(bname + "_" + bv + "_type", "variable");
// Get the number of bins explicitly
auto boundaries =
xmlBinning.attr<std::string>(std::string(bv + "boundaries").c_str());
std::string del = ",";
int end = boundaries.find(del);
int ib = 0;
// Unit conversion
double unitScalar = 1.;
if (bv != "phi") {
unitScalar = Acts::UnitConstants::mm / dd4hep::millimeter;
}
// Split and convert
while (end != -1) {
double bR = unitScalar * dd4hep::_toFloat(boundaries.substr(0, end));
variantParams.set<double>(
bname + "_" + bv + "_b" + std::to_string(ib++), bR);
boundaries.erase(boundaries.begin(), boundaries.begin() + end + 1);
end = boundaries.find(del);
}
double bR = unitScalar * std::stod(boundaries.substr(0, end));
variantParams.set<double>(bname + "_" + bv + "_b" + std::to_string(ib),
bR);
// The number of bins are needed to unpack the data
variantParams.set<int>(bname + "_" + bv + "_n", ib);
}
}
}

/// @brief This method converts the DD4hep binning into the Acts ProtoBinning
///
/// @param dd4hepElement the element which has a binning description attached
/// @param bname the binning base name, e.g. surface_binning, material_binning
///
/// @return a vector of proto binning descriptions
std::vector<Acts::Experimental::ProtoBinning> convertBinning(
const dd4hep::DetElement &dd4hepElement, const std::string &bname) {
std::vector<Experimental::ProtoBinning> protoBinnings;
for (const auto &[ab, bVal] : allowedBinnings) {
auto type =
getParamOr<std::string>(bname + "_" + ab + "_type", dd4hepElement, "");
if (not type.empty()) {
// Default binning is bound
auto bType = Acts::detail::AxisBoundaryType::Bound;
// Equidistant or variable binning
detail::AxisType aType = type == "equidistant"
? detail::AxisType::Equidistant
: detail::AxisType::Variable;
int nBins = getParamOr<int>(bname + "_" + ab + "_n", dd4hepElement, 0);
int nExpansion =
getParamOr<int>(bname + "_" + ab + "_exp", dd4hepElement, 0);
if (aType == detail::AxisType::Equidistant) {
// Equidistant binning
auto min = getParamOr<ActsScalar>(bname + "_" + ab + "_min",
dd4hepElement, 0.);
auto max = getParamOr<ActsScalar>(bname + "_" + ab + "_max",
dd4hepElement, 0.);
// Check for closed phi binning
if (bVal == binPhi and (max - min) > 1.9 * M_PI) {
bType = Acts::detail::AxisBoundaryType::Closed;
}
protoBinnings.push_back(Experimental::ProtoBinning(
bVal, bType, min, max, nBins, nExpansion));
} else {
// Variable binning
std::vector<ActsScalar> edges;
for (int ib = 0; ib <= nBins; ++ib) {
edges.push_back(getParamOr<ActsScalar>(
bname + "_" + ab + "_b" + std::to_string(ib), dd4hepElement, 0.));
}
// Check for closed phi binning
if (bVal == binPhi and (edges.back() - edges.front()) > 1.9 * M_PI) {
bType = Acts::detail::AxisBoundaryType::Closed;
}
protoBinnings.push_back(
Experimental::ProtoBinning(bVal, bType, edges, nExpansion));
}
}
}
return protoBinnings;
}

} // namespace Acts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#pragma once

#include <DD4hep/DetElement.h>
#include <DD4hep/DetFactoryHelper.h>
#include <DD4hep/Objects.h>
#include <DDRec/DetectorData.h>

namespace Acts {
Expand Down Expand Up @@ -88,4 +90,54 @@ inline bool hasParam(const std::string& key, dd4hep::DetElement& elt) {
inline bool hasParams(dd4hep::DetElement& elt) {
return elt.extension<dd4hep::rec::VariantParameters>(false) != nullptr;
}

/// @brief Helper method to get an attribute with fallback
///
/// @note the fallback value has to be provided
///
/// @tparam value_type the primitive type allowed by variant parameters
///
/// @param node the node object from DD4hep
/// @param attrName the name of the attribute that is checked
/// @param fallbackValue the fallbackValue
///
/// @return either the gathered attribute or the fallback
template <typename value_type>
value_type getAttrValueOr(const dd4hep::xml::Component& node,
const std::string& attrName,
const value_type& fallbackValue) {
if (node.hasAttr(dd4hep::xml::Strng_t(attrName.c_str()))) {
return node.attr<value_type>(attrName.c_str());
} else {
return fallbackValue;
}
}

/// @brief A simple helper function to extract a series
///
/// @tparam value_type the primitive type allowed by variant parameters
///
/// @param dd4hepElement the detector element w
/// @param bname The base name attribute of the variant parameter pack
/// @param unitConversion is a conversion factor DD4hep -> ACTS
///
/// @return the extracted series as a vector
template <typename value_type>
std::vector<value_type> extractSeries(const dd4hep::DetElement& dd4hepElement,
const std::string& bname,
const value_type& unitConversion = 1) {
std::vector<value_type> series = {};

int fallBack = 0;
int nVals = getParamOr<int>(bname + "_n", dd4hepElement, fallBack);
series.reserve(nVals);
for (auto ib = 0; ib < nVals; ++ib) {
auto val = unitConversion *
getParamOr<value_type>(bname + "_" + std::to_string(ib),
dd4hepElement, 0.);
series.push_back(val);
}
return series;
}

} // namespace Acts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Plugins/TGeo/TGeoDetectorElement.hpp"

#include <map>
#include <memory>
#include <string>

Expand All @@ -29,24 +30,25 @@ class ISurfaceMaterial;
///
/// DetectorElement plugin for DD4hep detector elements. DD4hep is based on
/// TGeo shapes, therefore the DD4hepDetectorElement inherits from
/// TGeoDetectorElement.
/// TGeoDetectorElement in order to perform the conversion.
///
/// The full geometrical information is provided by the TGeoDetectorElement.
/// The DD4hepDetectorElement extends the TGeoDetectorElement by containing a
/// segmentation for the readout.
/// @todo what if shape conversion fails? add implementation of more than one
/// surface per module, implementing also for other shapes->Cone,ConeSeg,Tube?
/// what
/// if not used with DD4hep?
/// @todo segmentation

///
class DD4hepDetectorElement : public TGeoDetectorElement {
public:
/// Broadcast the context type
using ContextType = GeometryContext;

/// Define a string based story
using Store = std::map<std::string,
std::vector<std::shared_ptr<DD4hepDetectorElement>>>;

/// Constructor
/// @param detElement The DD4hep DetElement which should be linked to a
/// surface
/// @param detElement The DD4hep DetElement which should be associated to
/// an ACTS surface
///
/// @param axes is the axis orientation with respect to the tracking frame
/// it is a string of the three characters x, y and z (standing for
/// the three axes) There is a distinction between
Expand All @@ -63,7 +65,6 @@ class DD4hepDetectorElement : public TGeoDetectorElement {
/// @param scalor is the scale factor for unit conversion if needed
/// @param isDisc in case the sensitive detector module should be translated
/// as disc (e.g. for endcaps) this flag should be set to true
/// @param digitizationModule Optional digitization configuration for the element
/// @note In the translation from a 3D geometry (TGeo) which only knows
/// tubes to a 2D geometry (Tracking geometry) a distinction if the
/// module should be described as a cylinder or a disc surface needs to
Expand All @@ -76,9 +77,7 @@ class DD4hepDetectorElement : public TGeoDetectorElement {
DD4hepDetectorElement(
const dd4hep::DetElement detElement, const std::string& axes = "XYZ",
double scalor = 1., bool isDisc = false,
std::shared_ptr<const ISurfaceMaterial> material = nullptr,
const std::shared_ptr<const DigitizationModule>& digitizationModule =
nullptr);
std::shared_ptr<const ISurfaceMaterial> material = nullptr);

~DD4hepDetectorElement() override = default;

Expand Down

0 comments on commit a3013a0

Please sign in to comment.