diff --git a/algo/ca/core/pars/CaField.cxx b/algo/ca/core/pars/CaField.cxx index fe9b2d71cf7daeef5933870abdc40a89a0d6233d..a4b24f596cb9f1c6064016a17ba08477457fe360 100644 --- a/algo/ca/core/pars/CaField.cxx +++ b/algo/ca/core/pars/CaField.cxx @@ -44,16 +44,10 @@ void FieldValue<DataT>::CheckConsistency() const //---------------------------------------------------------------------------------------------------------------------- // TODO: template<typename DataT> -std::string FieldValue<DataT>::ToString(int indentLevel) const +std::string FieldValue<DataT>::ToString(int) const { std::stringstream aStream{}; - constexpr char indentChar = '\t'; - std::string indent(indentLevel, indentChar); - aStream << indent << "Bx [kG]: " << std::setw(12) << std::setfill(' ') << kfutils::simd::Cast<DataT, float>(x) - << '\n'; - aStream << indent << "By [kG]: " << std::setw(12) << std::setfill(' ') << kfutils::simd::Cast<DataT, float>(y) - << '\n'; - aStream << indent << "Bz [kG]: " << std::setw(12) << std::setfill(' ') << kfutils::simd::Cast<DataT, float>(z); + aStream << "B = (" << x << ", " << y << ", " << z << ") [kG]"; return aStream.str(); } diff --git a/algo/ca/core/pars/CaField.h b/algo/ca/core/pars/CaField.h index 3921f298b8162f4f8c7c3b017265111c647e1d72..5aaeadbe9fe113961d39aa956bdd49a4ffd6067f 100644 --- a/algo/ca/core/pars/CaField.h +++ b/algo/ca/core/pars/CaField.h @@ -68,7 +68,7 @@ namespace cbm::algo::ca /// String representation of class contents /// \param indentLevel number of indent characters in the output - std::string ToString(int indentLevel) const; + std::string ToString(int indentLevel = 0) const; /// \brief Sets the entries of the field components into the i-th elements of the SIMD vectors /// \details If DataT is not a SIMD type, the function simply overwrites values ignoring the index i diff --git a/algo/kf/core/CMakeLists.txt b/algo/kf/core/CMakeLists.txt index 9247f6cf8c976e8da792e8ba2f333e5cb081e242..f412291aba2ff71cfc6c854e0ae73dc39a6dd4ba 100644 --- a/algo/kf/core/CMakeLists.txt +++ b/algo/kf/core/CMakeLists.txt @@ -17,6 +17,7 @@ set(SRCS ${CMAKE_CURRENT_SOURCE_DIR}/geo/KfFieldSlice.cxx ${CMAKE_CURRENT_SOURCE_DIR}/geo/KfFieldRegion.cxx ${CMAKE_CURRENT_SOURCE_DIR}/geo/KfSetup.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/geo/KfSetupInitializer.cxx ${CMAKE_CURRENT_SOURCE_DIR}/utils/KfUtils.cxx ) @@ -45,11 +46,12 @@ target_include_directories(KfCore target_compile_definitions(KfCore PUBLIC NO_ROOT) target_link_libraries(KfCore - PUBLIC Vc::Vc - Boost::serialization + PUBLIC Vc::Vc OnlineDataLog # needed for the logger external::fles_logging # needed for the logger - external::fles_ipc # needed for the logger + external::fles_ipc # needed for the logger + PRIVATE Boost::serialization + fmt::fmt external::yaml-cpp ) @@ -72,6 +74,7 @@ install( geo/KfFieldValue.h geo/KfMaterialMap.h geo/KfSetup.h + geo/KfSetupInitializer.h geo/KfTarget.h geo/KfField.h pars/KfParticlePDG.h diff --git a/algo/kf/core/KfDefs.h b/algo/kf/core/KfDefs.h index abed6a7468806ba882536440240e55162b3d3c4b..7a8a795b29da3eb98cac677b7d538b838f99c7f9 100644 --- a/algo/kf/core/KfDefs.h +++ b/algo/kf/core/KfDefs.h @@ -64,18 +64,13 @@ namespace cbm::algo::kf /// \brief Magnetic field function type /// Signature: tuple<Bx, By, Bz>(x, y, z); using FieldFn_t = std::function<std::tuple<double, double, double>(double, double, double)>; - - /// \brief Magnetic field function type - /// \note The field function follows the FairField::GetFieldFunction signature - using FieldFnFair_t = std::function<void(const double (&r)[3], double*)>; - } // namespace cbm::algo::kf namespace cbm::algo::kf::defs { // ----- Array sizes ------------------------------------------------------------------------------------------------- - constexpr int MaxNofFieldSlices = 64; ///< Max number of field slices - constexpr int MaxNofMaterialLayers = 32; ///< Max number of the material layers + //constexpr int MaxNofFieldSlices = 64; ///< Max number of field slices + //constexpr int MaxNofMaterialLayers = 32; ///< Max number of the material layers // ----- Control ----------------------------------------------------------------------------------------------------- constexpr int DebugLvl = 0; ///< Level of debug output diff --git a/algo/kf/core/geo/KfField.cxx b/algo/kf/core/geo/KfField.cxx index fc5ae8cd980673c5d8bc84e6329ce85d290172dc..888971655eea6e73566e0a7f148e390000cdd79c 100644 --- a/algo/kf/core/geo/KfField.cxx +++ b/algo/kf/core/geo/KfField.cxx @@ -16,45 +16,41 @@ using cbm::algo::kf::EFieldMode; using cbm::algo::kf::Field; using cbm::algo::kf::FieldFactory; -using cbm::algo::kf::FieldFnFair_t; using cbm::algo::kf::detail::FieldBase; -template<typename T> -using FieldBaseIntrpl_t = FieldBase<T, EFieldMode::Intrpl>; - -// --------------------------------------------------------------------------------------------------------------------- -// -template<typename T> -template<typename I> -FieldBaseIntrpl_t<T>::FieldBase(const FieldBaseIntrpl_t<I>& other) -{ - for (size_t i = 0; i < defs::MaxNofFieldSlices; ++i) { - fvFieldSlices[i] = utils::simd::Cast<I, T>(other.fvFieldSlices[i]); - } -} - // --------------------------------------------------------------------------------------------------------------------- // template<typename T, EFieldMode FldMode> -std::string Field<T, FldMode>::ToString(int indentLevel) const +std::string Field<T, FldMode>::ToString(int indentLevel, int verbose) const { constexpr char IndentChar = '\t'; std::stringstream msg; std::string indent(indentLevel, IndentChar); - msg << indent << "Field near primary vertex:\n" << this->fPrimVertexField.ToString(indentLevel + 1) << '\n'; + msg << indent << "Field near primary vertex:\n" << this->fPrimVertexField.ToString(indentLevel + 1, verbose) << '\n'; msg << indent << "Field type: " << static_cast<int>(this->fFieldType) << '\n'; if constexpr (FldMode == EFieldMode::Orig) { - msg << indent << "Original field function"; + //msg << indent << "Original field function"; } else if constexpr (FldMode == EFieldMode::Intrpl) { msg << indent << "Field slices:"; for (const auto& fldSlice : this->fvFieldSlices) { - msg << indent << "\n - " << fldSlice.ToString(indentLevel + 1); + msg << indent << "\n - " << fldSlice.ToString(indentLevel + 1, verbose); } } return msg.str(); } +namespace cbm::algo::kf +{ + template class Field<float, EFieldMode::Orig>; + template class Field<float, EFieldMode::Intrpl>; + template class Field<double, EFieldMode::Orig>; + template class Field<double, EFieldMode::Intrpl>; + template class Field<fvec, EFieldMode::Orig>; + template class Field<fvec, EFieldMode::Intrpl>; +} // namespace cbm::algo::kf + + // --------------------------------------------------------------------------------------------------------------------- // void FieldFactory::AddSliceReference(double halfSizeX, double halfSizeY, double zRef) @@ -71,12 +67,3 @@ void FieldFactory::AddSliceReference(double halfSizeX, double halfSizeY, double } } -namespace cbm::algo::kf -{ - template class Field<float, EFieldMode::Orig>; - template class Field<float, EFieldMode::Intrpl>; - template class Field<double, EFieldMode::Orig>; - template class Field<double, EFieldMode::Intrpl>; - template class Field<fvec, EFieldMode::Orig>; - template class Field<fvec, EFieldMode::Intrpl>; -} // namespace cbm::algo::kf diff --git a/algo/kf/core/geo/KfField.h b/algo/kf/core/geo/KfField.h index 98882d7c2286c4415860c7e1d292c3034aaf7896..c0bb840be05b85ad8df200e2a59f0130d3bdd63a 100644 --- a/algo/kf/core/geo/KfField.h +++ b/algo/kf/core/geo/KfField.h @@ -33,6 +33,9 @@ namespace cbm::algo::kf /// \tparam T Underlying floating point type (float/double/fvec) template<typename T> class alignas(VcMemAlign) FieldBase<T, EFieldMode::Orig> { + template<typename, EFieldMode> + friend class FieldBase; + public: using FieldRegion_t = FieldRegion<T, EFieldMode::Orig>; using FieldValue_t = FieldValue<T>; @@ -42,7 +45,7 @@ namespace cbm::algo::kf void SetFieldFunction(const FieldFn_t& fieldFn) { fFieldFn = fieldFn; } /// \brief Creates field region object - FieldRegion_t MakeFieldRegion() const { return FieldRegion_t(fFieldFn, fFieldType); } + FieldRegion_t MakeFieldRegion() const { return FieldRegion_t(fFieldType, fFieldFn); } protected: /// \brief Default constructor @@ -56,18 +59,7 @@ namespace cbm::algo::kf } /// \brief Copy assignment operator - /// \tparam I Underlying floating type of the source - template<typename I> - FieldBase& operator=(const FieldBase<I, EFieldMode::Orig>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fFieldFn = other.fFieldFn; - return *this; - } + FieldBase& operator=(const FieldBase& other) = default; FieldFn_t fFieldFn{defs::ZeroFieldFn}; ///< Field function: (x,y,z) [cm] -> (Bx,By,Bz) [kG] EFieldType fFieldType{EFieldType::Null}; ///< Field type @@ -88,12 +80,19 @@ namespace cbm::algo::kf /// \tparam T Underlying floating point type (float/double/fvec) template<typename T> class alignas(VcMemAlign) FieldBase<T, EFieldMode::Intrpl> { - using SlicesContainer_t = std::array<FieldSlice<T>, defs::MaxNofFieldSlices>; + template<typename, EFieldMode> + friend class FieldBase; + + using SlicesContainer_t = std::vector<FieldSlice<T>>; public: using FieldRegion_t = FieldRegion<T, EFieldMode::Intrpl>; using FieldValue_t = FieldValue<T>; + /// \brief Sets a field slice + /// \param fieldSlice Field slice object + void AddFieldSlice(const FieldSlice<T>& fieldSlice) { fvFieldSlices.push_back(fieldSlice); } + /// \brief Creates field value object /// \param sliceID Index of slice /// \param x x-coordinate of the point [cm] @@ -103,6 +102,9 @@ namespace cbm::algo::kf return fvFieldSlices[sliceID].GetFieldValue(x, y); } + /// \brief Gets number of field slices in the instance + int GetNofFieldSlices() const { return fvFieldSlices.size(); } + /// \brief Creates field region object /// \param b0 Field value in the first node [kG] /// \param z0 First node z-coordinate [cm] @@ -113,14 +115,9 @@ namespace cbm::algo::kf FieldRegion_t MakeFieldRegion(const FieldValue_t& b0, const T& z0, const FieldValue_t& b1, const T& z1, const FieldValue_t& b2, const T& z2) const { - return FieldRegion_t(b0, z0, b1, z1, b2, z2, fFieldType); + return FieldRegion_t(fFieldType, b0, z0, b1, z1, b2, z2); } - /// \brief Sets a field slice - /// \param id Index of the layer - /// \param fieldSlice Field slice object - void SetFieldSlice(int id, const FieldSlice<T>& fieldSlice) { fvFieldSlices[id] = fieldSlice; } - protected: /// \brief Default constructor FieldBase() = default; @@ -128,24 +125,18 @@ namespace cbm::algo::kf /// \brief Copy constructor /// \tparam I Underlying floating type of the source template<typename I> - FieldBase(const FieldBase<I, EFieldMode::Intrpl>& other); - - /// \brief Copy assignment operator - /// \tparam I Underlying floating type of the source - template<typename I> - FieldBase& operator=(const FieldBase<I, EFieldMode::Intrpl>& other) + FieldBase(const FieldBase<I, EFieldMode::Intrpl>& other) { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } + fvFieldSlices.clear(); + fvFieldSlices.reserve(other.fvFieldSlices.size()); + for (const auto& slice : other.fvFieldSlices) { + fvFieldSlices.emplace_back(FieldSlice<T>(slice)); } - for (size_t i = 0; i < defs::MaxNofFieldSlices; ++i) { - fvFieldSlices[i] = utils::simd::Cast<I, T>(other.fvFieldSlices[i]); - } - return *this; } + /// \brief Copy assignment operator + FieldBase& operator=(const FieldBase& other) = default; + SlicesContainer_t fvFieldSlices; ///< Array of field slices EFieldType fFieldType{EFieldType::Null}; ///< Field type @@ -167,12 +158,13 @@ namespace cbm::algo::kf /// \tparam T Underlying floating point type (float/double/fvec) template<typename T, EFieldMode FldMode> class alignas(VcMemAlign) Field : public detail::FieldBase<T, FldMode> { - using FieldBase_t = detail::FieldBase<T, FldMode>; + template<typename, EFieldMode> + friend class Field; public: static constexpr EFieldMode FieldType = FldMode; - using FieldRegion_t = typename FieldBase_t::FieldRegion_t; + using FieldRegion_t = FieldRegion<T, FldMode>; using Real_t = T; /// \brief Default constructor @@ -181,7 +173,7 @@ namespace cbm::algo::kf /// \brief Copy constructor template<typename I> Field(const Field<I, FldMode>& other) - : FieldBase_t(other) + : detail::FieldBase<T, FldMode>(other) , fPrimVertexField(FieldRegion<I, FldMode>(other.fPrimVertexField)) { } @@ -190,21 +182,14 @@ namespace cbm::algo::kf ~Field() = default; /// \brief Copy assignment operator - template<typename I> - Field& operator=(const Field<I, FldMode>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fPrimVertexField = other.fPrimVertexField; - return FieldBase_t::operator=(other); - } + Field& operator=(const Field& other) = default; /// \brief Gets field type EFieldType GetFieldType() const { return this->fFieldType; } + /// \brief Gets field region near primary vertex + const FieldRegion_t& GetPrimVertexField() const { return fPrimVertexField; } + /// \brief Sets field type /// \param fieldType Field type // TODO: SZh 21.08.2024: @@ -216,12 +201,10 @@ namespace cbm::algo::kf /// \param fld Field region near vertex void SetPrimVertexField(const FieldRegion_t& primVertexField) { fPrimVertexField = primVertexField; } - /// \brief Gets field region near primary vertex - const FieldRegion_t& GetPrimVertexField() const { return fPrimVertexField; } - /// \brief String representation of the class /// \param indentLevel Indent level of the string output - std::string ToString(int indentLevel) const; + /// \param verbose Verbosity level + std::string ToString(int indentLevel, int verbose) const; private: /// \brief Serialization function @@ -229,7 +212,7 @@ namespace cbm::algo::kf template<class Archive> void serialize(Archive& ar, const unsigned int) { - ar& boost::serialization::base_object<FieldBase_t>(*this); + ar& boost::serialization::base_object<detail::FieldBase<T, FldMode>>(*this); ar& fPrimVertexField; } @@ -292,10 +275,6 @@ namespace cbm::algo::kf /// \param fieldFn Magnetic field function (KF-format) void SetFieldFunction(const FieldFn_t& fieldFn) { fFieldFn = fieldFn; } - /// \brief Sets magnetic field function - /// \param fieldFn Magnetic field function (FairRoot format) - void SetFieldFunction(const FieldFnFair_t& fieldFn) { fFieldFn = GlobalField::ConvertFairFieldFunction(fieldFn); } - /// \brief Sets field type /// \param fieldType Field type void SetFieldType(EFieldType fieldType) { fFieldType = fieldType; } @@ -334,12 +313,6 @@ namespace cbm::algo::kf if (!fSliceReferences.size()) { // TODO: Remove requirement of slice references throw std::logic_error("FieldFactory::MakeField: no slice references were provided"); } - else if (fSliceReferences.size() > defs::MaxNofFieldSlices) { - std::stringstream msg; - msg << "FieldFactory::MakeField: too many slice references are provided (" << fSliceReferences.size() << "), " - << "the maximum allowed number of field slices is kf::defs::MaxNofFieldSlices = " << defs::MaxNofFieldSlices; - throw std::logic_error(msg.str()); - } if (!fFieldFn) { throw std::logic_error("FieldFactory::CreateField: no field function is provided"); } @@ -351,10 +324,8 @@ namespace cbm::algo::kf field.SetPrimVertexField(field.MakeFieldRegion()); } else if constexpr (FldMode == EFieldMode::Intrpl) { // A version with the interpolated field - int layerId = 0; for (const auto& sliceRef : fSliceReferences) { - field.SetFieldSlice(layerId++, - FieldSlice<T>(fFieldFn, sliceRef.fHalfSizeX, sliceRef.fHalfSizeY, sliceRef.fRefZ)); + field.AddFieldSlice(FieldSlice<T>(fFieldFn, sliceRef.fHalfSizeX, sliceRef.fHalfSizeY, sliceRef.fRefZ)); } { // PV field initialization @@ -362,7 +333,7 @@ namespace cbm::algo::kf double z = fTarget[2]; std::array<FieldValue<T>, nNodes> fld; std::array<T, nNodes> pos; - for (int iNode = 0; iNode < nNodes; ++iNode) { + for (size_t iNode = 0; iNode < nNodes; ++iNode) { pos[iNode] = utils::simd::Cast<double, T>(z); fld[iNode] = GlobalField::GetFieldValue<double>(fFieldFn, fTarget[0], fTarget[1], z); z += fTargetStep; diff --git a/algo/kf/core/geo/KfFieldRegion.cxx b/algo/kf/core/geo/KfFieldRegion.cxx index 54d3cc53e6ed1696662c1967aea8d43b36a707db..fc892352463994563cd665a5adca6256732a1b8d 100644 --- a/algo/kf/core/geo/KfFieldRegion.cxx +++ b/algo/kf/core/geo/KfFieldRegion.cxx @@ -15,15 +15,15 @@ #include <mutex> #include <sstream> +#include <fmt/format.h> + using cbm::algo::kf::EFieldMode; using cbm::algo::kf::FieldFn_t; -using cbm::algo::kf::FieldFnFair_t; using cbm::algo::kf::FieldRegion; using cbm::algo::kf::FieldValue; using cbm::algo::kf::GlobalField; -template<typename T> -using FieldRegionIntrpl_t = cbm::algo::kf::detail::FieldRegionBase<T, EFieldMode::Intrpl>; +using cbm::algo::kf::detail::FieldRegionBase; FieldFn_t GlobalField::gOriginalField = &GlobalField::UndefField; @@ -32,8 +32,8 @@ FieldFn_t GlobalField::gOriginalField = &GlobalField::UndefField; // --------------------------------------------------------------------------------------------------------------------- // template<typename T> -void FieldRegionIntrpl_t<T>::Set(const FieldValue<T>& b0, const T& z0, const FieldValue<T>& b1, const T& z1, - const FieldValue<T>& b2, const T& z2) +void FieldRegionBase<T, EFieldMode::Intrpl>::Set(const FieldValue<T>& b0, const T& z0, const FieldValue<T>& b1, + const T& z1, const FieldValue<T>& b2, const T& z2) { fZfirst = z0; @@ -58,7 +58,7 @@ void FieldRegionIntrpl_t<T>::Set(const FieldValue<T>& b0, const T& z0, const Fie // --------------------------------------------------------------------------------------------------------------------- // template<typename T> -void FieldRegionIntrpl_t<T>::Shift(const T& z) +void FieldRegionBase<T, EFieldMode::Intrpl>::Shift(const T& z) { auto dz = z - fZfirst; for (int iD = 0; iD < 3; ++iD) { @@ -70,6 +70,13 @@ void FieldRegionIntrpl_t<T>::Shift(const T& z) fZfirst = z; } +namespace cbm::algo::kf::detail +{ + template class FieldRegionBase<float, EFieldMode::Intrpl>; + template class FieldRegionBase<double, EFieldMode::Intrpl>; + template class FieldRegionBase<fvec, EFieldMode::Intrpl>; +} // namespace cbm::algo::kf::detail + // --------------------------------------------------------------------------------------------------------------------- // template<typename T, EFieldMode FldMode> @@ -92,21 +99,32 @@ FieldValue<T> FieldRegion<T, FldMode>::Get(const T& x, const T& y, const T& z) c // --------------------------------------------------------------------------------------------------------------------- // template<typename T, EFieldMode FldMode> -std::string FieldRegion<T, FldMode>::ToString(int indentLevel) const +std::string FieldRegion<T, FldMode>::ToString(int indentLevel, int) const { constexpr char IndentChar = '\t'; std::stringstream msg; std::string indent(indentLevel, IndentChar); if constexpr (FldMode == EFieldMode::Intrpl) { + using fmt::format; + auto Cnv = [&](const auto& val) { return utils::simd::Cast<T, Literal_t<T>>(val); }; // alias for the conversion fn const auto& coeff = this->fCoeff; - msg << indent << "Field region: dz = z - " << this->fZfirst << " [cm]\n"; - msg << indent << " Bx(dz) = " << coeff[0][0] << " + " << coeff[0][1] << "dz + " << coeff[0][2] << "dz2\n"; - msg << indent << " By(dz) = " << coeff[1][0] << " + " << coeff[1][1] << "dz + " << coeff[1][2] << "dz2\n"; - msg << indent << " Bz(dz) = " << coeff[2][0] << " + " << coeff[2][1] << "dz + " << coeff[2][2] << "dz2"; + msg << indent << format("Field Region: dz = z - ({})", Cnv(this->fZfirst)); + msg << '\n' + << indent << IndentChar + << format("Bx(dz) = {:>12} + {:>12} dz + {:>12} dz2", Cnv(coeff[0][0]), Cnv(coeff[0][1]), Cnv(coeff[0][2])); + msg << '\n' + << indent << IndentChar + << format("Bx(dz) = {:>12} + {:>12} dz + {:>12} dz2", Cnv(coeff[1][0]), Cnv(coeff[1][1]), Cnv(coeff[1][2])); + msg << '\n' + << indent << IndentChar + << format("Bx(dz) = {:>12} + {:>12} dz + {:>12} dz2", Cnv(coeff[2][0]), Cnv(coeff[2][1]), Cnv(coeff[2][2])); if constexpr (defs::dbg::ForceOriginalField) { - msg << indent << "\nWARNING: the defs::dbg::ForceOriginalField is enabled, so the magnetic field interpolation " - << "is replaced with the global magnetic function for debugging purposes. If you see this message and are " - << "not sure, what is going on, please set the defs::dbg::ForceOriginalField to false and re-run the routine"; + msg + << indent << "\nWARNING: the defs::dbg::ForceOriginalField is enabled, so the magnetic field interpolation " + << indent + << "\nis replaced with the global magnetic function for debugging purposes. If you see this message and are " + << indent + << "\nnot sure, what is going on, please set the defs::dbg::ForceOriginalField to false and re-run the routine"; } } else if constexpr (FldMode == EFieldMode::Orig) { @@ -126,25 +144,6 @@ std::tuple<double, double, double> GlobalField::UndefField(double, double, doubl return std::make_tuple(defs::Undef<double>, defs::Undef<double>, defs::Undef<double>); } -// --------------------------------------------------------------------------------------------------------------------- -// -FieldFn_t GlobalField::ConvertFairFieldFunction(const FieldFnFair_t& fn) -{ - if (!fn) { - throw std::runtime_error("GlobalField::ConvertFairFieldFunction(): attempt to set an undefined field function"); - } - - return [&](double x, double y, double z) -> std::tuple<double, double, double> { - double pos[3] = {x, y, z}; - double fld[3] = {0.}; - static std::mutex mtx; - mtx.lock(); - fn(pos, fld); - mtx.unlock(); - return std::make_tuple(fld[0], fld[1], fld[2]); - }; -} - namespace cbm::algo::kf { diff --git a/algo/kf/core/geo/KfFieldRegion.h b/algo/kf/core/geo/KfFieldRegion.h index d7f40d39b117084427671f1f039770572c7cde14..9e8ceec5e44f2744a66fc03b480992f8c8a52ceb 100644 --- a/algo/kf/core/geo/KfFieldRegion.h +++ b/algo/kf/core/geo/KfFieldRegion.h @@ -38,11 +38,7 @@ namespace cbm::algo::kf /// \brief Sets global field function /// \param fn Field function of the FairRoot format - static void SetFieldFunction(const FieldFnFair_t& fn) { gOriginalField = ConvertFairFieldFunction(fn); } - - /// \brief Converts a FairRoot field function to the KF format - /// \param fn Field function of the FairRoot format - static FieldFn_t ConvertFairFieldFunction(const FieldFnFair_t& fn); + static void SetFieldFunction(const FieldFn_t& fn) { gOriginalField = fn; } /// \brief Creates a field value object in a spactial point of a generic floating-point type /// \tparam T Floating point data-type of the involved variables @@ -56,7 +52,6 @@ namespace cbm::algo::kf const T& z); }; - namespace detail { /// \class FieldRegionBase @@ -74,6 +69,9 @@ namespace cbm::algo::kf /// \tparam T Underlying floating point type (float/double/fvec) template<typename T> class alignas(VcMemAlign) FieldRegionBase<T, EFieldMode::Orig> { + template<typename, EFieldMode> + friend class FieldRegionBase; + public: using FieldValue_t = FieldValue<T>; @@ -94,18 +92,7 @@ namespace cbm::algo::kf ~FieldRegionBase() = default; /// \brief Copy assignment operator - /// \tparam I Underlying floating point type of the source - template<typename I> - FieldRegionBase& operator=(const FieldRegionBase<I, EFieldMode::Orig>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fFieldFn = other.fFieldFn; - return *this; - } + FieldRegionBase& operator=(const FieldRegionBase& other) = default; protected: FieldFn_t fFieldFn{defs::ZeroFieldFn}; ///< Field function: (x,y,z) [cm] -> (Bx,By,Bz) [kG] @@ -125,6 +112,9 @@ namespace cbm::algo::kf /// \tparam T Underlying floating point type (float/double/fvec) template<typename T> class alignas(VcMemAlign) FieldRegionBase<T, EFieldMode::Intrpl> { + template<typename, EFieldMode> + friend class FieldRegionBase; + using CoeffArray_t = std::array<std::array<T, 3>, 3>; ///< Approximation coefficients array public: @@ -149,28 +139,20 @@ namespace cbm::algo::kf /// \brief Copy constructor template<typename I> FieldRegionBase(const FieldRegionBase<I, EFieldMode::Intrpl>& other) - : fCoeff(utils::simd::Cast<I, T>(other.fCoeff)) - , fZfirst(utils::simd::Cast<I, T>(other.fZfirst)) + : fZfirst(utils::simd::Cast<I, T>(other.fZfirst)) { + for (size_t iDim = 0; iDim < fCoeff.size(); ++iDim) { + for (size_t iPow = 0; iPow < fCoeff[iDim].size(); ++iPow) { + fCoeff[iDim][iPow] = utils::simd::Cast<I, T>(other.fCoeff[iDim][iPow]); + } + } } /// \brief Destructor ~FieldRegionBase() = default; /// \brief Copy assignment operator - /// \tparam I Underlying floating point type of the source - template<typename I> - FieldRegionBase& operator=(const FieldRegionBase<I, EFieldMode::Orig>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - this->fCoeff = utils::simd::Cast<I, T>(other.fCoeff); - this->fZfirst = utils::simd::Cast<I, T>(other.fZfirst); - return *this; - } + FieldRegionBase& operator=(const FieldRegionBase& other) = default; /// \brief Gets the first z-coordinate of the interpolation const T& GetZfirst() const { return fZfirst; } @@ -213,7 +195,8 @@ namespace cbm::algo::kf /// \tparam FldMode Field type template<typename T, EFieldMode FldMode> class alignas(VcMemAlign) FieldRegion : public detail::FieldRegionBase<T, FldMode> { - using FieldRegionBase_t = detail::FieldRegionBase<T, FldMode>; + template<typename, EFieldMode> + friend class FieldRegion; public: static constexpr EFieldMode FieldType = FldMode; @@ -227,16 +210,17 @@ namespace cbm::algo::kf /// \brief Copy constructor /// \tparam I Underlying floating point type of the source template<typename I> - FieldRegion(const FieldRegion<I, FldMode>& other) : FieldRegionBase_t(other) + FieldRegion(const FieldRegion<I, FldMode>& other) : detail::FieldRegionBase<T, FldMode>(other) { } /// \brief Constructor from the arguments - /// \param args Arguments, which have to be passed to the base class constructors /// \param fieldType Type of the field + /// \param args Arguments, which have to be passed to the base class constructors template<typename... Args> - FieldRegion(Args... args, EFieldType fieldType) : FieldRegionBase_t(args...) - , fFieldType(fieldType) + FieldRegion(EFieldType fieldType, Args... args) + : detail::FieldRegionBase<T, FldMode>(args...) + , fFieldType(fieldType) { } @@ -244,18 +228,7 @@ namespace cbm::algo::kf ~FieldRegion() = default; /// \brief Copy assignment operator - /// \tparam I Underlying floating point type of the source - template<typename I> - FieldRegion& operator=(const FieldRegion<I, FldMode>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fFieldType = other.fFieldType; - return FieldRegionBase_t::operator=(other); - } + FieldRegion& operator=(const FieldRegion& other) = default; /// \brief Gets the field value in a spatial point on the track /// \param x x-coordinate of the point [cm] @@ -269,7 +242,8 @@ namespace cbm::algo::kf /// \brief String representation of the class content /// \param intdentLevel Indent level - std::string ToString(int indentLevel = 0) const; + /// \param verbose Verbosity level + std::string ToString(int indentLevel = 0, int verbose = 1) const; private: /// \brief Serialization method @@ -277,7 +251,8 @@ namespace cbm::algo::kf template<class Archive> void serialize(Archive& ar, const unsigned int) { - ar& boost::serialization::base_object<FieldRegionBase_t>(*this); + using boost::serialization::base_object; + ar& base_object<detail::FieldRegionBase<T, FldMode>>(*this); ar& fFieldType; } diff --git a/algo/kf/core/geo/KfFieldSlice.cxx b/algo/kf/core/geo/KfFieldSlice.cxx index b5f94447294ab8898fe5a37a97b43ddade9d4a01..8e8989ea1c8d0c212853abe841efafc92e7ae243 100644 --- a/algo/kf/core/geo/KfFieldSlice.cxx +++ b/algo/kf/core/geo/KfFieldSlice.cxx @@ -12,6 +12,8 @@ #include <iomanip> #include <sstream> +#include <fmt/format.h> + using cbm::algo::kf::FieldFn_t; using cbm::algo::kf::FieldSlice; using cbm::algo::kf::FieldValue; @@ -53,8 +55,8 @@ FieldSlice<T>::FieldSlice(const FieldFn_t& fieldFn, double xMax, double yMax, do double xFactor = x; int k = ((kPolDegree - 1) * (kPolDegree + 2)) / 2; // index of the monomial vector element for (int i = kPolDegree; i > 0; --i) { // loop over y powers - for (int j = kNofCoeff; j >= kNofCoeff - i; --j) { // loop over x powers - m[k] = xFactor * m[j]; + for (int j = kNofCoeff - 1; j >= kNofCoeff - i; --j) { // loop over x powers + m[k--] = xFactor * m[j]; } xFactor *= x; } @@ -96,44 +98,47 @@ FieldSlice<T>::FieldSlice(const FieldFn_t& fieldFn, double xMax, double yMax, do } } - for (int iD = 0; iD < 3; ++iD) { - auto& coeff = fCoeff[iD]; - for (int j = 0; j < kNofCoeff; ++j) { - coeff[j] = utils::simd::Cast<double, T>(A[j][kNofCoeff + iD] / A[j][j]); - } + for (int j = 0; j < kNofCoeff; ++j) { + fBx[j] = utils::simd::Cast<double, T>(A[j][kNofCoeff] / A[j][j]); + fBy[j] = utils::simd::Cast<double, T>(A[j][kNofCoeff + 1] / A[j][j]); + fBz[j] = utils::simd::Cast<double, T>(A[j][kNofCoeff + 2] / A[j][j]); } } // --------------------------------------------------------------------------------------------------------------------- // template<typename T> -std::string FieldSlice<T>::ToString(int indentLevel) const +std::string FieldSlice<T>::ToString(int indentLevel, int verbose) const { - using std::setw; + using fmt::format; constexpr char indentChar = '\t'; std::stringstream msg; std::string indent(indentLevel, indentChar); + auto Cnv = [&](const auto& val) { return utils::simd::Cast<T, Literal_t<T>>(val); }; // alias for the conversion fn - std::array<std::string, kNofCoeff> m; - { - m[kNofCoeff - 1] = ""; - for (int i = kNofCoeff - 2; i >= kNofCoeff - 1 - kPolDegree; --i) { - m[i] = std::string("y") + std::to_string(kNofCoeff - i - 1); - } - int k = ((kPolDegree - 1) * (kPolDegree + 2)) / 2; // index of the monomial vector element - for (int i = kPolDegree; i > 0; --i) { // loop over y powers - std::string x = std::string("x") + std::to_string(kPolDegree - i + 1); - for (int j = kNofCoeff; j >= kNofCoeff - i; --j) { // loop over x powers - m[k] = x + m[j]; + if (verbose > 0) { + msg << indent << format("FieldSlice: zRef = {} cm", Cnv(fZref)); + if (verbose > 1) { + std::array<std::string, kNofCoeff> m; + { + m[kNofCoeff - 1] = ""; + for (int i = kNofCoeff - 2; i >= kNofCoeff - 1 - kPolDegree; --i) { + m[i] = format("y{}", kNofCoeff - i - 1); + } + int k = ((kPolDegree - 1) * (kPolDegree + 2)) / 2; // index of the monomial vector element + for (int i = kPolDegree; i > 0; --i) { // loop over y powers + std::string x = format("x{}", kPolDegree - i + 1); + for (int j = kNofCoeff - 1; j >= kNofCoeff - i; --j) { // loop over x powers + m[k--] = x + m[j]; + } + } + m[kNofCoeff - 1] = "1"; + } + msg << '\n' << indent << format("{:>15} {:>15} {:>15} {:>15}", "Monomial", "Bx", "By", "Bz"); + for (int i = 0; i < kNofCoeff; ++i) { + msg << '\n' << indent << format("{:>15} {:>15} {:>15} {:>15}", m[i], Cnv(fBx[i]), Cnv(fBy[i]), Cnv(fBz[i])); } } - m[kNofCoeff - 1] = "1"; - } - msg << indent << setw(15) << "Monomial" << ' ' << setw(15) << "Bx" << ' ' << setw(15) << "By" << ' ' << setw(15) - << "Bz\n"; - for (int i = 0; i < kNofCoeff; ++i) { - msg << indent << setw(15) << m[i] << ' ' << setw(15) << fCoeff[0][i] << ' ' << setw(15) << fCoeff[1][i] << ' ' - << setw(15) << fCoeff[2][i] << '\n'; } return msg.str(); } diff --git a/algo/kf/core/geo/KfFieldSlice.h b/algo/kf/core/geo/KfFieldSlice.h index 11fc02ad6b5df4a5378b4e99e9541573180e31eb..295051c2423da08d86d1fe574f0b18f876ea65bc 100644 --- a/algo/kf/core/geo/KfFieldSlice.h +++ b/algo/kf/core/geo/KfFieldSlice.h @@ -29,11 +29,14 @@ namespace cbm::algo::kf /// \tparam T Underlying data-type template<typename T> class alignas(VcMemAlign) FieldSlice { + template<typename> + friend class FieldSlice; + static constexpr int kPolDegree{5}; ///< Approximation polynomial degree static constexpr int kNofCoeff{((kPolDegree + 1) * (kPolDegree + 2)) / 2}; ///< Number of coefficients - /// \brief Array of the approximation coefficients [<dimension>][<index>] - using CoeffArray_t = std::array<std::array<T, kNofCoeff>, 3>; + /// \brief Array of the approximation coefficients [<monomial>] + using CoeffArray_t = std::array<T, kNofCoeff>; public: /// \brief Default constructor @@ -50,8 +53,10 @@ namespace cbm::algo::kf /// \tparam I The underlying data type of the source object template<typename I> FieldSlice(const FieldSlice<I>& other) - : fCoeff(utils::simd::Cast(other.fCoeff)) - , fZref(utils::simd::Cast(other.fZref)) + : fBx(utils::simd::Cast<I, T, kNofCoeff>(other.fBx)) + , fBy(utils::simd::Cast<I, T, kNofCoeff>(other.fBy)) + , fBz(utils::simd::Cast<I, T, kNofCoeff>(other.fBz)) + , fZref(utils::simd::Cast<I, T>(other.fZref)) { } @@ -59,16 +64,7 @@ namespace cbm::algo::kf ~FieldSlice() = default; /// \brief Copy assignment operator - /// \tparam I The underlying data type of the source object - template<typename I> - FieldSlice& operator=(const FieldSlice<I>& other) - { - if (this != &other) { - this->fCoeff = utils::simd::Cast<I, T>(other.fCoeff); - this->fZref = utils::simd::Cast<I, T>(other.fZref); - } - return *this; - } + FieldSlice& operator=(const FieldSlice& other) = default; /// \brief Gets field value at a point on the transverse plane /// \param x x-coordinate of the point [cm] @@ -78,9 +74,9 @@ namespace cbm::algo::kf { using math::Horner; /* clang-format off */ - return FieldValue<T>(Horner<kPolDegree>(fCoeff[0].cbegin(), x, y), - Horner<kPolDegree>(fCoeff[1].cbegin(), x, y), - Horner<kPolDegree>(fCoeff[2].cbegin(), x, y)); + return FieldValue<T>(Horner<kPolDegree>(fBx.cbegin(), x, y), + Horner<kPolDegree>(fBy.cbegin(), x, y), + Horner<kPolDegree>(fBz.cbegin(), x, y)); /* clang-format on */ } @@ -98,7 +94,8 @@ namespace cbm::algo::kf /// \brief String representation of the class content /// \param intdentLevel Indent level - std::string ToString(int indentLevel = 0) const; + /// \param verbose Verbosity level + std::string ToString(int indentLevel = 0, int verbose = 1) const; private: /// \brief Serialization function @@ -106,11 +103,15 @@ namespace cbm::algo::kf template<class Archive> void serialize(Archive& ar, const unsigned int) { - ar& fCoeff; + ar& fBx; + ar& fBy; + ar& fBz; ar& fZref; } - CoeffArray_t fCoeff{{T(0.)}}; ///< Approximation coefficients for the field slice - T fZref{defs::Undef<T>}; ///< Reference z of the coefficients [cm] + CoeffArray_t fBx{{T(0.)}}; ///< Approximation coefficients for the x-component of the field + CoeffArray_t fBy{{T(0.)}}; ///< Approximation coefficients for the y-component of the field + CoeffArray_t fBz{{T(0.)}}; ///< Approximation coefficients for the z-component of the field + T fZref{defs::Undef<T>}; ///< Reference z of the coefficients [cm] }; } // namespace cbm::algo::kf diff --git a/algo/kf/core/geo/KfFieldValue.h b/algo/kf/core/geo/KfFieldValue.h index b26a3791b5c4ff3ea8d701857a9c64db899d3c0d..b4b1a8fc22c7832f11c2ae89f2fdd66faef6cd42 100644 --- a/algo/kf/core/geo/KfFieldValue.h +++ b/algo/kf/core/geo/KfFieldValue.h @@ -56,15 +56,7 @@ namespace cbm::algo::kf ~FieldValue() = default; /// \brief Copy assignment operator - /// \tparam I Underlying data-type of the other object - template<typename I> - FieldValue& operator=(const FieldValue<I>& other) - { - if (this != &other) { - this->fB = utils::simd::Cast<I, T, 3>(other.fB); - } - return *this; - } + FieldValue& operator=(const FieldValue& other) = default; /// \brief Combines the current magnetic field value with another one using a mask /// \param other Other flux value diff --git a/algo/kf/core/geo/KfMaterialMap.cxx b/algo/kf/core/geo/KfMaterialMap.cxx index e6f69ce164128064de0ad604d36060374053e48d..5708155e66ef5d3d1ad01b096ccfa021b7bb73da 100644 --- a/algo/kf/core/geo/KfMaterialMap.cxx +++ b/algo/kf/core/geo/KfMaterialMap.cxx @@ -12,6 +12,8 @@ #include <sstream> #include <vector> +#include <fmt/format.h> + using cbm::algo::kf::fvec; using cbm::algo::kf::MaterialMap; @@ -50,24 +52,24 @@ void MaterialMap::Add(const MaterialMap& other, float zTarg) { // The function allows to add a material layer either from the left or from the right to the station // NOTE: A symmetry of x-y is assumed - constexpr int nRays = 3; // Number of rays in a bin in a dimension - const bool bRadialRays = !std::isnan(zTarg); // Are rays radial (true, coming from target) or parallel (false)? - const auto scaleFactor = bRadialRays ? ((other.fZref - zTarg) / (this->fZref - zTarg)) : 1.; - const auto binSize = 2. * scaleFactor * this->fXYmax / this->fNbins; // Size of each bin dimension [cm] - const auto stepSize = binSize / static_cast<float>(nRays); // Step between two neighboring rays [cm] + constexpr int nRays{100}; // Number of rays in a bin in a dimension + const bool bRadialRays{!std::isnan(zTarg)}; // Are rays radial (true, coming from target) or parallel (false)? + const auto scaleFactor{bRadialRays ? ((other.fZref - zTarg) / (this->fZref - zTarg)) : 1.F}; + const auto binSize{2.F * scaleFactor * this->fXYmax / this->fNbins}; // Size of each bin dimension [cm] + const auto stepSize{binSize / static_cast<float>(nRays)}; // Step between two neighboring rays [cm] // The coordinates of the first ray intersection with the other material layer [cm] - float xBinOther = -this->fXYmax * scaleFactor + stepSize * 0.5; - float yBinOther = xBinOther; + float xBinOther{-this->fXYmax * scaleFactor + stepSize * 0.5F}; + float yBinOther{xBinOther}; // Loop over bins of the active (this) - for (int iBinY = 0; iBinY < this->fNbins; ++iBinY) { - for (int iBinX = 0; iBinX < this->fNbins; ++iBinX) { + for (int iBinY{0}; iBinY < this->fNbins; ++iBinY) { + for (int iBinX{0}; iBinX < this->fNbins; ++iBinX) { // Collect material using ray shooting - float avgThickness = 0; // Collected average thickness - for (int iRayY = 0; iRayY < nRays; ++iRayY) { - for (int iRayX = 0; iRayX < nRays; ++iRayX) { - avgThickness += other.GetRadThick(xBinOther + iBinX * stepSize, yBinOther + iBinY * stepSize); + float avgThickness{0}; // Collected average thickness + for (int iRayY{0}; iRayY < nRays; ++iRayY) { + for (int iRayX{0}; iRayX < nRays; ++iRayX) { + avgThickness += other.GetThickness(xBinOther + iBinX * stepSize, yBinOther + iBinY * stepSize); } } this->fTable[iBinX + this->fNbins * iBinY] += avgThickness / (nRays * nRays); @@ -83,14 +85,49 @@ void MaterialMap::Add(const MaterialMap& other, float zTarg) // int MaterialMap::GetBin(float x, float y) const { - int i = static_cast<int>((x + fXYmax) * fFactor); - int j = static_cast<int>((y + fXYmax) * fFactor); + int i{static_cast<int>((x + fXYmax) * fFactor)}; + int j{static_cast<int>((y + fXYmax) * fFactor)}; if (i < 0 || j < 0 || i >= fNbins || j >= fNbins) { return -1; } return i + j * fNbins; } +// --------------------------------------------------------------------------------------------------------------------- +// +void MaterialMap::Rebin(int nGroups) +{ + if (nGroups < 1 && nGroups > fNbins) { + int nGroupsSet{nGroups}; + nGroups = std::clamp(nGroups, 1, fNbins); + LOG(warn) << "kf::MaterialMap::Rebin(): incorrect input parameter nGroups = " << nGroupsSet << " is set to " + << nGroups; + } + + int nBinsNew = static_cast<int>(static_cast<bool>(fNbins % nGroups)) + fNbins / nGroups; + std::vector<float> table(nBinsNew * nBinsNew); + for (int iX{0}; iX < nBinsNew; ++iX) { + for (int iY{0}; iY < nBinsNew; ++iY) { + float value{0.F}; + int nEntries{0}; + int iOldXmin{iX * nGroups}; + int iOldXmax{std::max((iX + 1) * nGroups, fNbins)}; + int iOldYmin{iY * nGroups}; + int iOldYmax{std::max((iY + 1) * nGroups, fNbins)}; + for (int iOldX{iOldXmin}; iOldX < iOldXmax; ++iOldX) { + for (int iOldY{iOldYmin}; iOldY < iOldYmax; ++iOldY) { + value += fTable[iOldX + fNbins * iOldY]; + ++nEntries; + } + } + table[iX + nBinsNew * iY] = value / nEntries; + } + } + fTable = std::move(table); + fFactor = fFactor / fNbins * nBinsNew; + fNbins = nBinsNew; +} + // --------------------------------------------------------------------------------------------------------------------- // void MaterialMap::Swap(MaterialMap& other) noexcept @@ -106,12 +143,38 @@ void MaterialMap::Swap(MaterialMap& other) noexcept // --------------------------------------------------------------------------------------------------------------------- // -std::string MaterialMap::ToString() const +std::string MaterialMap::ToString(int indentLevel, int verbose) const { - std::stringstream msg; + using fmt::format; using std::setw; - msg << "[mat.map] nBins " << setw(6) << fNbins << ", XYmax " << setw(12) << fXYmax << " , z (ref, min, max) " - << setw(12) << fZref << ' ' << setw(12) << fZmin << ' ' << setw(12) << fZmax; + std::stringstream msg; + if (verbose > 0) { + constexpr char indentCh = '\t'; + std::string indent(indentLevel, indentCh); + msg << indent + << format("zRef = {:<12} cm, range [{:<12}, {:<12}] cm, nBins = {:<8}, XYmax = {:<12} cm", fZref, fZmin, fZmax, + fNbins, fXYmax); + if (verbose > 1) { + msg << indent << indentCh << "\nContent(rebinned to 10 bins):\n"; + msg.precision(3); + auto mapTmp{*this}; + mapTmp.Rebin(fNbins / 10); + msg << indent << indentCh << setw(15) << "y [cm] \\ x [cm]" << ' '; + for (int iX{0}; iX < mapTmp.fNbins; ++iX) { + float xRef{-mapTmp.fXYmax + (mapTmp.fXYmax * (2 * iX + 1)) / mapTmp.fNbins}; + msg << indent << indentCh << setw(8) << xRef << ' '; + } + msg << '\n'; + for (int iY{0}; iY < mapTmp.fNbins; ++iY) { + float yRef{-mapTmp.fXYmax + (mapTmp.fXYmax * (2 * iY + 1)) / mapTmp.fNbins}; + msg << indent << indentCh << setw(15) << yRef << ' '; + for (int iX{0}; iX < mapTmp.fNbins; ++iX) { + msg << indent << indentCh << setw(8) << mapTmp.fTable[iX + mapTmp.fNbins * iY] << ' '; + } + msg << '\n'; + } + } + } return msg.str(); } diff --git a/algo/kf/core/geo/KfMaterialMap.h b/algo/kf/core/geo/KfMaterialMap.h index 30289c882bdc9628c2bd2d945f86a3f2f550a26f..46e40f00922f9b6ce8e409bbb51f895c8cc463a1 100644 --- a/algo/kf/core/geo/KfMaterialMap.h +++ b/algo/kf/core/geo/KfMaterialMap.h @@ -72,26 +72,17 @@ namespace cbm::algo::kf /// \brief Gets maximal Z of the collected material [cm] float GetZmax() const { return fZmax; } - /// \brief Gets value of X/X0 in a given cell of the material table by the indeces of the row and column - /// \param iBinX Index of table column - /// \param iBinY Index of table row - float GetRadThickBin(int iBinX, int iBinY) const { return fTable[iBinX + fNbins * iBinY]; } - - /// \brief Gets value of X/X0 in a given cell of the material table by the index of the bin - /// \param iBin Index of the bin in 2d table - float GetRadThickBin(int iBin) const { return fTable[iBin]; } - - /// \brief Gets material thickness in units of X0 in (x,y) point of the station + /// \brief Gets material thickness in units of radiational length X0 /// \tparam I Type of the x and y (floating point) /// \param x X coordinate of the point [cm] /// \param y Y coordinate of the point [cm] template<typename I> - I GetRadThick(const I& x, const I& y) const + I GetThickness(const I& x, const I& y) const { if constexpr (std::is_same_v<I, fvec>) { fvec res; for (size_t i = 0; i < utils::simd::Size<I>(); ++i) { - res[i] = GetRadThick(x[i], y[i]); + res[i] = GetThickness(x[i], y[i]); } return res; } @@ -112,6 +103,10 @@ namespace cbm::algo::kf return utils::IsUndefined(fNbins) || utils::IsUndefined(fXYmax * fFactor * fZref * fZmin * fZmax); } + /// \brief Reduces number of bins by a given factor + /// \param factor Number of bins in a new bin + void Rebin(int factor); + /// \brief Sets value of material thickness in units of X0 for a given cell of the material table /// \param iBinX Index of table column /// \param iBinY Index of table row @@ -124,11 +119,10 @@ namespace cbm::algo::kf /// \brief Swap method void Swap(MaterialMap& other) noexcept; - /// \brief Get bin index for (x,y). Returns -1 when outside of the map - int GetBin(float x, float y) const; - /// \brief String representation of the object - std::string ToString() const; + /// \param indentLevel Indent level of the string output + /// \param verbose Verbosity level + std::string ToString(int indentLevel = 0, int verbose = 1) const; /// \brief Comparison operator (material map ordering by fZref) friend bool operator<(const MaterialMap& lhs, const MaterialMap& rhs) { return lhs.fZref < rhs.fZref; } @@ -138,6 +132,18 @@ namespace cbm::algo::kf /// \throw std::logic_error If the object is in non-valid mode void CheckConsistency() const; + /// \brief Get bin index for (x,y). Returns -1 when outside of the map + int GetBin(float x, float y) const; + + /// \brief Gets material thickness in units of radiational length X0 + /// \param iBin Index of the bin in 2d table + float GetThickness(int iBin) const { return fTable[iBin]; } + + /// \brief Gets material thickness in units of radiational length X0 + /// \param iBinX Index of table column + /// \param iBinY Index of table row + float GetThickness(int iBinX, int iBinY) const { return fTable[iBinX + fNbins * iBinY]; } + int fNbins = defs::Undef<int>; ///< Number of rows (== N columns) in the material budget table float fXYmax = defs::Undef<float>; ///< Size of the station in x and y dimensions [cm] float fFactor = defs::Undef<float>; ///< Util. var. for the conversion of point coordinates to row/column id diff --git a/algo/kf/core/geo/KfSetup.cxx b/algo/kf/core/geo/KfSetup.cxx index af7b923f2b59382013524fa515920584f0e4f35a..ec8862775685506164e4325da359e7d930ba9096 100644 --- a/algo/kf/core/geo/KfSetup.cxx +++ b/algo/kf/core/geo/KfSetup.cxx @@ -10,14 +10,9 @@ #include "KfSetup.h" #include <sstream> +#include <typeinfo> using cbm::algo::kf::Setup; -using cbm::algo::kf::SetupInitializer; - -// -// Setup methods definition -// - // --------------------------------------------------------------------------------------------------------------------- // @@ -28,112 +23,28 @@ std::string Setup<T>::ToString(int verbosity, int indentLevel) const if (verbosity > 0) { constexpr char indentCh = '\t'; std::string indent(indentLevel, indentCh); - msg << indent << "----- KF setup -----\n"; - msg << indent << "Target:\n" << fTarget.ToString(indentLevel + 1); - msg << indent << "Material layers:\n"; + msg << indent << "\n ----- KF Setup -----\n"; + msg << indent << "\nFloating-point type: " << typeid(T).name(); + + msg << indent << "\nTARGET:\n" << fTarget.ToString(indentLevel + 1, verbosity); + msg << indent << "\nMATERIAL LAYERS:\n"; for (const auto& layer : fvMaterialLayers) { - msg << indent << indentCh << layer.ToString() << '\n'; + msg << layer.ToString(indentLevel + 1, verbosity) << '\n'; } - msg << indent << "Interpolated magnetic field:\n" << fIntrplField.ToString(indentLevel + 1); + msg << indent << "\nINTERPOLATED MAGNETIC FIELD:\n" << fIntrplField.ToString(indentLevel + 1, verbosity); if (foOrigField) { - msg << indent << "Original magnetic field:\n" << foOrigField->ToString(indentLevel + 1); + msg << indent << "\nORIGINAL MAGNETIC FIELD:\n" << foOrigField->ToString(indentLevel + 1, verbosity); } else { - msg << indent << "Original magnetic field: not provided"; + msg << indent << "\nORIGINAL MANGETIC FIELD: not provided"; } } return msg.str(); } - namespace cbm::algo::kf { template class Setup<float>; template class Setup<double>; template class Setup<fvec>; } // namespace cbm::algo::kf - -// -// SetupInitializer methods definition -// - -// --------------------------------------------------------------------------------------------------------------------- -// -void SetupInitializer::AddMaterial(const MaterialMap& material) -{ - if (!fMaterialLayers.emplace(material).second) { - std::stringstream msg; - msg << "SetupInitializer::AddMaterial: attempt of adding a duplicating material layer " << material.ToString() - << ".\nThe next material layers were already added:"; - for (const auto& el : fMaterialLayers) { - msg << "\n\t- " << el.ToString(); - } - throw std::logic_error(msg.str()); - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// -template<typename T> -Setup<T> SetupInitializer::MakeSetup(bool bProvideOrigField) const -{ - Setup<T> setup; - // Target initialization - setup.SetTarget(fTarget); - - // Field initialization - setup.SetField(fFieldFactory.MakeField<T, EFieldMode::Intrpl>()); - if (bProvideOrigField) { - setup.SetField(fFieldFactory.MakeField<T, EFieldMode::Orig>()); - } - - // Material layers initialization - if (fMaterialLayers.size() > defs::MaxNofMaterialLayers) { - std::stringstream msg; - msg << "kf::SetupInitializer::MakeSetup(): too many material layers are provided (" << fMaterialLayers.size() - << "), the maximum allowed number of the layers kf::defs::MaxNofMaterialLayers = " - << defs::MaxNofMaterialLayers; - throw std::logic_error(msg.str()); - } - int layerId = 0; - for (const auto& material : fMaterialLayers) { - setup.SetMaterial(layerId, material); - } - - return setup; -} - -// --------------------------------------------------------------------------------------------------------------------- -// -void SetupInitializer::Reset() -{ - fFieldFactory.Reset(); - fMaterialLayers.clear(); -} - -// --------------------------------------------------------------------------------------------------------------------- -// -void SetupInitializer::SetTargetProperties(double x, double y, double z, double fieldInitStep, - const MaterialMap& material) -{ - fTarget.SetX(x); - fTarget.SetY(y); - fTarget.SetZ(z); - fTarget.SetMaterial(material); - fFieldFactory.SetTarget(x, y, z); - fFieldFactory.SetStep(fieldInitStep); -} - -// --------------------------------------------------------------------------------------------------------------------- -// -void SetupInitializer::Store(const Setup<double>& setup, const std::string& fileName) -{ - std::ofstream ofs(fileName, std::ios::binary); - if (!ofs) { - std::stringstream msg; - msg << "kf::SetupInitializer::Store: failed openning file \"" << fileName << "\" to store the setup"; - throw std::runtime_error(msg.str()); - } - boost::archive::binary_oarchive oa(ofs); - oa << setup; -} diff --git a/algo/kf/core/geo/KfSetup.h b/algo/kf/core/geo/KfSetup.h index 7cced174893dfce18170b7ba611abaecd15b0d78..75829aa2e64b4f53d9b0a2938a60d1a9c175e7c4 100644 --- a/algo/kf/core/geo/KfSetup.h +++ b/algo/kf/core/geo/KfSetup.h @@ -15,8 +15,6 @@ #include "KfTarget.h" #include "KfVector.h" -#include <boost/archive/binary_iarchive.hpp> -#include <boost/archive/binary_oarchive.hpp> #include <boost/serialization/access.hpp> #include <boost/serialization/split_free.hpp> @@ -34,10 +32,9 @@ namespace cbm::algo::kf template<typename T> class alignas(VcMemAlign) Setup { friend class boost::serialization::access; // Boost serializer methods - template<typename I> + template<typename> friend class Setup; - using MaterialContainer_t = std::array<MaterialMap, defs::MaxNofMaterialLayers>; public: /// \brief Default constructor @@ -51,8 +48,8 @@ namespace cbm::algo::kf template<typename I> Setup(const Setup<I>& other) : fvMaterialLayers(other.fvMaterialLayers) - , foOrigField(other.foOrigField.has_value() ? std::make_optional(*other.foOrigField) : std::nullopt) , fIntrplField(other.fIntrplField) + , foOrigField(other.foOrigField.has_value() ? std::make_optional(*other.foOrigField) : std::nullopt) , fTarget(other.fTarget) { } @@ -61,25 +58,15 @@ namespace cbm::algo::kf //Setup(Setup&&) noexcept; /// \brief Copy assignment operator - /// \tparam I Underlying floating-point data type of the source - template<typename I> - Setup& operator=(const Setup<I>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fvMaterialLayers = other.fvMaterialLayers; - foOrigField = other.foOrigField; - fIntrplField = other.fIntrplField; - fTarget = other.fTarget; - return *this; - } + Setup& operator=(const Setup& other) = default; /// \brief Move assignment operator //Setup& operator=(Setup&&) noexcept; + /// \brief Sets material layer + /// \param map Material map + void AddMaterial(const MaterialMap& map) { fvMaterialLayers.emplace_back(map); } + /// \brief Makes an instance of the field depending on the template parameter /// \tparam FldMode Field mode of the returned instance /// \throw std::logic_error If the particular field member is undefined (nullopt) @@ -90,23 +77,21 @@ namespace cbm::algo::kf /// \param iLayer Index of layer const MaterialMap& GetMaterial(int iLayer) const { return fvMaterialLayers[iLayer]; } + /// \brief Gets number of defined material layers + int GetNofMaterialLayers() const { return static_cast<int>(fvMaterialLayers.size()); } + /// \brief Gets target const Target<T>& GetTarget() const { return fTarget; } /// \brief String representation of the class contents /// \param verbosity A verbose level for output /// \param indentLevel Indent level of the string output - std::string ToString(int verbosity = 0, int indentLevel = 0) const; + std::string ToString(int verbosity = 1, int indentLevel = 0) const; /// \brief Sets field template<EFieldMode FldMode> void SetField(const Field<double, FldMode>& field); - /// \brief Sets material layer - /// \param iLayer Index of layer - /// \param map Material map - void SetMaterial(int iLayer, const MaterialMap& map) { fvMaterialLayers[iLayer] = map; } - /// \brief Sets target (moves from the source) /// \param target Target instance void SetTarget(const Target<double>& target) { fTarget = target; } @@ -117,6 +102,7 @@ namespace cbm::algo::kf void load(Archive& ar, const unsigned int /*version*/) { ar >> fvMaterialLayers; + ar >> fvMaterialLayerIds; ar >> fTarget; ar >> fIntrplField; foOrigField = std::nullopt; @@ -129,6 +115,7 @@ namespace cbm::algo::kf void save(Archive& ar, const unsigned int /*version*/) const { ar << fvMaterialLayers; + ar << fvMaterialLayerIds; ar << fTarget; ar << fIntrplField; } @@ -139,109 +126,13 @@ namespace cbm::algo::kf boost::serialization::split_member(ar, *this, version); } - MaterialContainer_t fvMaterialLayers = {}; ///< Container of the material maps - Field<T, EFieldMode::Intrpl> fIntrplField; ///< Interpolated field (NOTE: maybe make optional) + std::vector<MaterialMap> fvMaterialLayers = {}; ///< Container of the material maps + std::vector<int> fvMaterialLayerIds = {}; ///< Map of the layer ids: original -> actual + Field<T, EFieldMode::Intrpl> fIntrplField; ///< Interpolated field (NOTE: maybe make optional) std::optional<Field<T, EFieldMode::Orig>> foOrigField{std::nullopt}; ///< Optional original field Target<T> fTarget; ///< Target layer }; - - /// \class SetupInitializer - /// \brief Creates a valid initialized Setup instance - class SetupInitializer { - public: - /// \brief Default constructor - SetupInitializer() = default; - - /// \brief Copy constructor - SetupInitializer(const SetupInitializer&) = delete; - - /// \brief Move constructor - SetupInitializer(SetupInitializer&&) = delete; - - /// \brief Destructor - virtual ~SetupInitializer() = default; - - /// \brief Copy assignment operator - SetupInitializer& operator=(const SetupInitializer&) = delete; - - /// \brief Move assignment operator - SetupInitializer& operator=(SetupInitializer&&) = delete; - - /// \brief Adds magnetic field slice reference - /// \param halfSizeX Half-size of the slice in x-direction [cm] - /// \param halfSizeY Half-size of the slice in y-direction [cm] - /// \param refZ Reference z-position of the slice [cm] - void AddFieldSliceReference(double halfSizeX, double halfSizeY, double refZ) - { - fFieldFactory.AddSliceReference(halfSizeX, halfSizeY, refZ); - } - - /// \brief Adds material layer - /// \param material Material layer - void AddMaterial(const MaterialMap& material); - - /// \brief Virtual function, which initializes a KF-setup - /// \return true Initialization was successful - /// \return false Initialization failed - /// - /// This function can be re-implemented for a particular use-case... - virtual bool InitProperties() { return false; } - - /// \brief Creates a setup instance - /// \param bProvideOrigField Flag: true - defines optional variable of the original field - template<typename T> - Setup<T> MakeSetup(bool bProvideOrigField = true) const; - - /// \brief Resets the instance - void Reset(); - - /// \brief Sets magnetic field function - /// \param fieldFn Magnetic field function (either KF, or FairRoot format) - /// \param fieldType Magnetic field type - template<typename FieldFn> - void SetFieldFunction(const FieldFn& fieldFn, EFieldType fieldType) - { - fFieldFactory.SetFieldFunction(fieldFn); - fFieldFactory.SetFieldType(fieldType); - } - - /// \brief Sets target initialization properties - /// \param x Target x-coordinate [cm] - /// \param y Target y-coordinate [cm] - /// \param z Target z-coordinate [cm] - /// \param fieldInitStep Step between the nodal points in the target field region initialization [cm] - /// \param material Target material layer - void SetTargetProperties(double x, double y, double z, double fieldInitStep, const MaterialMap& material); - - /// \brief Stores a serialized setup to a file - /// \param setup A reference to the Setup (NOTE: a double-Setup must be passed explicitly) - /// \param fileName Output file name - /// - /// Only a Setup with T = double can be serialized, so if a particular setup instance has another floating-point - /// type, it will be converted to the double-version. In the serialization only the interpolated magnetic field - /// variant is stored, the original field version will be set to nullopt during the loading from the file. - static void Store(const Setup<double>& setup, const std::string& fileName); - - /// \brief Loads a serialized setup from a file - /// \tparam T Underlying floating-point type for the output setup object - /// \param fileName Input file name - /// \throw std::runtime_error If the fileName cannot be opened - template<typename T> - static Setup<T> Load(const std::string& fileName); - - private: - std::set<MaterialMap> fMaterialLayers{}; ///< Set of material maps - FieldFactory fFieldFactory; ///< Instance of field factory - Target<double> fTarget; ///< Field to keep target properties - bool fbPropertiesInitialized{false}; ///< Flag: if all the necessary fields are initialized - }; - - - // ******************************** - // ** Template method definition ** - // ******************************** - // ------------------------------------------------------------------------------------------------------------------- // template<typename T> @@ -277,31 +168,4 @@ namespace cbm::algo::kf foOrigField = std::make_optional(field); } } - - // ------------------------------------------------------------------------------------------------------------------- - // - template<typename T> - Setup<T> SetupInitializer::Load(const std::string& fileName) - { - Setup<double> setup; - std::ifstream ifs(fileName, std::ios::binary); - if (!ifs) { - std::stringstream msg; - msg << "kf::SetupInitializer::Load: intput setup file \"" << fileName << "\" was not found"; - throw std::runtime_error(msg.str()); - } - try { - boost::archive::binary_iarchive ia(ifs); - ia >> setup; - } - catch (const std::exception& err) { - std::stringstream msg; - msg << "kf::SetupInitializer::Load: input setup file \"" << fileName - << "\" has inconsistent format or was " - "corrupted. The exception message: " - << err.what(); - throw std::runtime_error(msg.str()); - } - return Setup<T>(setup); - } } // namespace cbm::algo::kf diff --git a/algo/kf/core/geo/KfSetupInitializer.cxx b/algo/kf/core/geo/KfSetupInitializer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3148dc428d71061c8d647bc3a383da7f9c94d333 --- /dev/null +++ b/algo/kf/core/geo/KfSetupInitializer.cxx @@ -0,0 +1,63 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// @file KfSetupInitializer.h +/// @brief A base KF-Setup initialization class (source) +/// @since 28.03.2024 +/// @author Sergei Zharko <s.zharko@gsi.de> + +#include "KfSetupInitializer.h" + +using cbm::algo::kf::SetupInitializer; + + +// --------------------------------------------------------------------------------------------------------------------- +// +void SetupInitializer::AddMaterial(const MaterialMap& material) +{ + if (!fMaterialLayers.emplace(material).second) { + std::stringstream msg; + msg << "SetupInitializer::AddMaterial: attempt of adding a duplicating material layer " << material.ToString() + << ".\nThe next material layers were already added:"; + for (const auto& el : fMaterialLayers) { + msg << "\n\t- " << el.ToString(); + } + throw std::logic_error(msg.str()); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void SetupInitializer::Reset() +{ + fFieldFactory.Reset(); + fMaterialLayers.clear(); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void SetupInitializer::SetTargetProperties(double x, double y, double z, double fieldInitStep, + const MaterialMap& material) +{ + fTarget.SetX(x); + fTarget.SetY(y); + fTarget.SetZ(z); + fTarget.SetMaterial(material); + fFieldFactory.SetTarget(x, y, z); + fFieldFactory.SetStep(fieldInitStep); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void SetupInitializer::Store(const Setup<double>& setup, const std::string& fileName) +{ + std::ofstream ofs(fileName, std::ios::binary); + if (!ofs) { + std::stringstream msg; + msg << "kf::SetupInitializer::Store: failed openning file \"" << fileName << "\" to store the setup"; + throw std::runtime_error(msg.str()); + } + boost::archive::binary_oarchive oa(ofs); + oa << setup; +} diff --git a/algo/kf/core/geo/KfSetupInitializer.h b/algo/kf/core/geo/KfSetupInitializer.h new file mode 100644 index 0000000000000000000000000000000000000000..7fb84e9f6bd413d0da7eafe3c2393eabf79dff46 --- /dev/null +++ b/algo/kf/core/geo/KfSetupInitializer.h @@ -0,0 +1,169 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// @file KfSetupInitializer.h +/// @brief A base KF-Setup initialization class (header) +/// @since 28.03.2024 +/// @author Sergei Zharko <s.zharko@gsi.de> + +#pragma once // include this header only once per compilation unit + +#include "KfSetup.h" + +#include <boost/archive/binary_iarchive.hpp> +#include <boost/archive/binary_oarchive.hpp> + +#include <fstream> +#include <optional> +#include <sstream> +#include <string> +#include <vector> + +namespace cbm::algo::kf +{ + /// \class SetupInitializer + /// \brief Creates a valid initialized Setup instance + class SetupInitializer { + public: + /// \brief Default constructor + SetupInitializer() = default; + + /// \brief Copy constructor + SetupInitializer(const SetupInitializer&) = delete; + + /// \brief Move constructor + SetupInitializer(SetupInitializer&&) = delete; + + /// \brief Destructor + virtual ~SetupInitializer() = default; + + /// \brief Copy assignment operator + SetupInitializer& operator=(const SetupInitializer&) = delete; + + /// \brief Move assignment operator + SetupInitializer& operator=(SetupInitializer&&) = delete; + + /// \brief Adds magnetic field slice reference + /// \param halfSizeX Half-size of the slice in x-direction [cm] + /// \param halfSizeY Half-size of the slice in y-direction [cm] + /// \param refZ Reference z-position of the slice [cm] + void AddFieldSliceReference(double halfSizeX, double halfSizeY, double refZ) + { + fFieldFactory.AddSliceReference(halfSizeX, halfSizeY, refZ); + } + + /// \brief Adds material layer + /// \param material Material layer + void AddMaterial(const MaterialMap& material); + + /// \brief Virtual function, which initializes a KF-setup + /// \return true Initialization was successful + /// \return false Initialization failed + /// + /// This function can be re-implemented for a particular use-case... + virtual bool InitProperties() { return false; } + + /// \brief Creates a setup instance + /// \param bProvideOrigField Flag: true - defines optional variable of the original field + template<typename T> + Setup<T> MakeSetup(bool bProvideOrigField = true) const; + + /// \brief Resets the instance + void Reset(); + + /// \brief Sets magnetic field function + /// \param fieldFn Magnetic field function + /// \param fieldType Magnetic field type + void SetFieldFunction(const FieldFn_t& fieldFn, EFieldType fieldType) + { + fFieldFactory.SetFieldFunction(fieldFn); + fFieldFactory.SetFieldType(fieldType); + } + + /// \brief Sets target initialization properties + /// \param x Target x-coordinate [cm] + /// \param y Target y-coordinate [cm] + /// \param z Target z-coordinate [cm] + /// \param fieldInitStep Step between the nodal points in the target field region initialization [cm] + /// \param material Target material layer + void SetTargetProperties(double x, double y, double z, double fieldInitStep, const MaterialMap& material); + + /// \brief Stores a serialized setup to a file + /// \param setup A reference to the Setup (NOTE: a double-Setup must be passed explicitly) + /// \param fileName Output file name + /// + /// Only a Setup with T = double can be serialized, so if a particular setup instance has another floating-point + /// type, it will be converted to the double-version. In the serialization only the interpolated magnetic field + /// variant is stored, the original field version will be set to nullopt during the loading from the file. + static void Store(const Setup<double>& setup, const std::string& fileName); + + /// \brief Loads a serialized setup from a file + /// \tparam T Underlying floating-point type for the output setup object + /// \param fileName Input file name + /// \throw std::runtime_error If the fileName cannot be opened + template<typename T> + static Setup<T> Load(const std::string& fileName); + + private: + std::set<MaterialMap> fMaterialLayers{}; ///< Set of material maps + FieldFactory fFieldFactory; ///< Instance of field factory + Target<double> fTarget; ///< Field to keep target properties + bool fbPropertiesInitialized{false}; ///< Flag: if all the necessary fields are initialized + }; + + + // ******************************** + // ** Template method definition ** + // ******************************** + + // --------------------------------------------------------------------------------------------------------------------- + // + template<typename T> + Setup<T> SetupInitializer::MakeSetup(bool bProvideOrigField) const + { + Setup<T> setup; + // Target initialization + setup.SetTarget(fTarget); + + // Field initialization + setup.SetField(fFieldFactory.MakeField<double, EFieldMode::Intrpl>()); + if (bProvideOrigField) { + setup.SetField(fFieldFactory.MakeField<double, EFieldMode::Orig>()); + } + + // Material layers initialization + for (const auto& material : fMaterialLayers) { + setup.AddMaterial(material); + } + + return setup; + } + + // ------------------------------------------------------------------------------------------------------------------- + // + template<typename T> + Setup<T> SetupInitializer::Load(const std::string& fileName) + { + Setup<double> setup; + std::ifstream ifs(fileName, std::ios::binary); + if (!ifs) { + std::stringstream msg; + msg << "kf::SetupInitializer::Load: intput setup file \"" << fileName << "\" was not found"; + throw std::runtime_error(msg.str()); + } + try { + boost::archive::binary_iarchive ia(ifs); + ia >> setup; + } + catch (const std::exception& err) { + std::stringstream msg; + msg << "kf::SetupInitializer::Load: input setup file \"" << fileName + << "\" has inconsistent format or was " + "corrupted. The exception message: " + << err.what(); + throw std::runtime_error(msg.str()); + } + return Setup<T>(setup); + } +} // namespace cbm::algo::kf diff --git a/algo/kf/core/geo/KfTarget.cxx b/algo/kf/core/geo/KfTarget.cxx index 1df0856d6dee7072171f69d1cc9b84afad73e81e..6f4864d1253ea565f5845588e2a29941f4007a41 100644 --- a/algo/kf/core/geo/KfTarget.cxx +++ b/algo/kf/core/geo/KfTarget.cxx @@ -39,13 +39,14 @@ void Target<T>::SetMaterial(const MaterialMap& material) // --------------------------------------------------------------------------------------------------------------------- // template<typename T> -std::string Target<T>::ToString(int indentLevel) const +std::string Target<T>::ToString(int indentLevel, int verbose) const { constexpr char IndentChar = '\t'; std::stringstream msg; std::string indent(indentLevel, IndentChar); - msg << indent << "position: {" << fX << ", " << fY << ", " << fZ << "} [cm]\n"; - msg << indent << "material: " << fMaterial.ToString(); + auto Cnv = [&](const auto& val) { return utils::simd::Cast<T, Literal_t<T>>(val); }; // alias for the conversion fn + msg << indent << "position: {" << Cnv(fX) << ", " << Cnv(fY) << ", " << Cnv(fZ) << "} [cm]\n"; + msg << indent << "material:\n" << indent << IndentChar << " " << fMaterial.ToString(verbose); return msg.str(); } diff --git a/algo/kf/core/geo/KfTarget.h b/algo/kf/core/geo/KfTarget.h index 60734745845fab50562d28b8841e832a7657aa01..51ab42ed1c2db490cfd251d647e8f38399cf24b3 100644 --- a/algo/kf/core/geo/KfTarget.h +++ b/algo/kf/core/geo/KfTarget.h @@ -52,21 +52,7 @@ namespace cbm::algo::kf ~Target() = default; /// \brief Copy assignment operator - /// \tparam I Underlying floating-point type of the source - template<typename I> - Target& operator=(const Target<I>& other) - { - if constexpr (std::is_same_v<I, T>) { - if (this == &other) { - return *this; - } - } - fMaterial = other.fMaterial; - fX = utils::simd::Cast<I, T>(other.fX); - fY = utils::simd::Cast<I, T>(other.fY); - fZ = utils::simd::Cast<I, T>(other.fZ); - return *this; - } + Target& operator=(const Target& other) = default; /// \brief Gets x-coordinate of the nominal target center const T& GetX() const { return fX; } @@ -98,7 +84,8 @@ namespace cbm::algo::kf /// \brief String representation of the class /// \param indentLevel Indent level of the string output - std::string ToString(int indentLevel = 0) const; + /// \param verbose Verbosity level + std::string ToString(int indentLevel = 0, int vebose = 1) const; private: /// \brief Serialization method diff --git a/algo/kf/core/utils/KfUtils.h b/algo/kf/core/utils/KfUtils.h index 2f4c277f46804f90ed57b18b623ea0f16f17b41e..7d88186ec1d38e90669d218ebabb6927e7da6a0b 100644 --- a/algo/kf/core/utils/KfUtils.h +++ b/algo/kf/core/utils/KfUtils.h @@ -247,6 +247,26 @@ namespace cbm::algo::kf::utils::simd return static_cast<DataOut>(val); } + template<typename DataT, typename DataOut, size_t N> + inline std::array<DataOut, N> Cast(const std::array<DataT, N>& arr, size_t i) + { + std::array<DataOut, N> res; + for (size_t iEl = 0; iEl < arr.size(); ++iEl) { + res[iEl] = Cast<DataT, DataOut>(arr[iEl], i); + } + return res; + } + + template<typename DataT, typename DataOut, size_t N> + inline std::array<DataOut, N> Cast(const std::array<DataT, N>& arr) + { + std::array<DataOut, N> res; + for (size_t iEl = 0; iEl < arr.size(); ++iEl) { + res[iEl] = Cast<DataT, DataOut>(arr[iEl]); + } + return res; + } + /// \brief Converts a value of type DataT at a specific index to type DataOut /// \details This specialization extracts the element at the specified index of the input SIMD float vector (fvec) and returns it as a float /// \param val Input value of type fvec to be converted @@ -269,24 +289,6 @@ namespace cbm::algo::kf::utils::simd return static_cast<double>(val[i]); } - /// \brief Converts a value of type DataT at a specific index to type DataOut - /// \details This function is a specialization for the array conversions - /// \tparam DataT Input value data type - /// \tparam DataOut Converted value data type - /// \tparam N Size of the array - /// \param val Input value of type DataT to be converted - /// \param i The index indicating which element to convert, ignored for non-SIMD DataT - /// \return Return val as DataOut - template<typename DataT, typename DataOut, size_t N> - inline std::array<DataOut, N> Cast(const std::array<DataT, N>& in, size_t i) - { - std::array<DataOut, N> ret; - for (size_t j = 0; j < N; ++j) { - ret[j] = Cast<DataT, DataOut>(in[j], i); - } - return ret; - } - /// \brief Returns the number of elements available for a given data type /// \details This function is a template that provides a default implementation returning 1 /// \tparam DataT The data type for which the size is determined diff --git a/algo/kf/tools/CMakeLists.txt b/algo/kf/tools/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3a072d4a7390a40f9c69e21ecf66a206c2e931bb --- /dev/null +++ b/algo/kf/tools/CMakeLists.txt @@ -0,0 +1,61 @@ +set(INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set(SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/KfMaterialMapCreator.cxx +) + +SET_SOURCE_FILES_PROPERTIES(${SRCS} PROPERTIES COMPILE_FLAGS "-O3") + + +If(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + ADD_DEFINITIONS(-Wall -Wextra -Wsign-promo -Wctor-dtor-privacy -Wreorder -Wno-deprecated -Wno-parentheses) # -Weffc++ -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast : wait for other parts of cbmroot\root. +Else() + ADD_DEFINITIONS(-Wall -Wextra -Wsign-promo -Wno-pmf-conversions -Wctor-dtor-privacy -Wreorder -Wno-deprecated -Wstrict-null-sentinel -Wno-non-template-friend -Wno-parentheses -Wmissing-field-initializers) # -Weffc++ -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast : wait for other parts of cbmroot\root. +EndIf() + +add_library(KfTools SHARED ${SRCS}) + +#list(APPEND HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/utils/KfSimd.h) + +target_include_directories(KfTools + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} +) + +#target_compile_definitions(KfTools PUBLIC NO_ROOT) + +target_link_libraries(KfTools + PUBLIC FairRoot::Base + FairLogger::FairLogger + ROOT::Core + ROOT::Gpad + ROOT::Graf + ROOT::Physics + fmt::fmt + PRIVATE KfCore + FairRoot::GeoBase + FairRoot::ParBase + ROOT::EG + ROOT::Geom + ROOT::Graf3d + ROOT::MathCore + ROOT::Matrix + ROOT::Minuit + ROOT::RIO + ) + +install(TARGETS KfTools DESTINATION lib) +#install(DIRECTORY kf TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +#install(DIRECTORY kf/utils TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +#install(DIRECTORY kf/data TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +#install(DIRECTORY kf/geo TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +#install(DIRECTORY kf/algo TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +#install(DIRECTORY kf/pars TYPE INCLUDE FILES_MATCHING PATTERN "*.h") + +install( + FILES + KfMaterialMapCreator.h + DESTINATION + include/ +) diff --git a/algo/kf/tools/KfMaterialMapCreator.cxx b/algo/kf/tools/KfMaterialMapCreator.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b138863da49ce115fc504c84a900a4d9ec9fc171 --- /dev/null +++ b/algo/kf/tools/KfMaterialMapCreator.cxx @@ -0,0 +1,407 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file KfMaterialMapCreator.cxx +/// \brief Utility to generate material budget map from the TGeoNavigator representation of the Setup (implementation) +/// \author Sergey Gorbunov <se.gorbunov@gsi.de> +/// \author Sergei Zharko <s.zharko@gsi.de> +/// \date 29.08.2024 + +#include "KfMaterialMapCreator.h" + +#include "KfMaterialMap.h" +#include "TGeoManager.h" +#include "TGeoMedium.h" +#include "TGeoNavigator.h" +#include "TGeoNode.h" +#include "TGeoPhysicalNode.h" +#include "TGeoVoxelFinder.h" + +#include <Logger.h> + +#include <sstream> +#include <thread> + +using cbm::algo::kf::MaterialMap; +using kf::tools::MaterialMapCreator; + +// --------------------------------------------------------------------------------------------------------------------- +// +MaterialMapCreator::MaterialMapCreator(int verbose) : fVerbose(verbose) +{ + // constructor + + // save the current number of threads to restore it when the work is done + fNthreadsOld = gGeoManager->GetMaxThreads(); + + // set defaut n threads to 1 + + fNthreads = 1; + + std::stringstream msg; + msg << "\n------------------------------------------------------------------------------------"; + msg << "\n Material budget map creator"; + msg << '\n'; + msg << "\n !! To run it with the full speed, set: \"export OMP_NUM_THREADS=<n CPUs>\" before running !!"; + msg << '\n'; + // if possible, read the allowed n threads from the environment variable + { + const char* env = std::getenv("OMP_NUM_THREADS"); + if (env) { + fNthreads = TString(env).Atoi(); + msg << "\n found environment variable OMP_NUM_THREADS = \"" << env << "\", read as integer: " << fNthreads; + } + } + + // ensure that at least one thread is set + + if (fNthreads < 1) { + fNthreads = 1; + } + + msg << "\n Maps will be created using " << fNthreads << " CPU threads"; + msg << '\n'; + msg << "\n It might crash because of a ROOT bug. In this case do one of the following: "; + msg << '\n'; + msg << "\n - fix illegal overlaps in the geometry via gGeoManager->CheckOverlaps()"; + msg << "\n - run with CbmL1::SetSafeMaterialInitialization() option. "; + msg << "\n It fixes the crash, but slows down the initialization significantly."; + msg << "\n - manually disable voxel finders for problematic volumes in CaToolsMaterialMapCreator.cxx"; + msg << '\n'; + msg << "\n------------------------------------------------------------------------------------"; + LOG_IF(info, fVerbose > 0) << msg.str(); + + InitThreads(); + + // + // It looks like TGeoVoxelFinder contains a bug and therefore sometimes produces a crash. + // + // Currently, the crash only happens on some of TGeoVolumeAssembly volumes + // and only when an (inproper?) alignment matrices are applied. + // + // Here we try to catch this case and disable those finders. + // + // Disabling all the voxel finders leads to a very long initialization, try to avoid it! + // + // TODO: make a bug report to the ROOT team; remove the following code once the bug is fixed. + // + + if (fDoSafeInitialization) { // disable all voxel finders + TObjArray* volumes = gGeoManager->GetListOfVolumes(); + if (volumes) { + for (int iv = 0; iv < volumes->GetEntriesFast(); iv++) { + TGeoVolume* vol = dynamic_cast<TGeoVolume*>(volumes->At(iv)); + if (!vol) { + continue; + } + TGeoVoxelFinder* finder = vol->GetVoxels(); + if (finder) { + finder->SetInvalid(); + } + } + } + } + else { // disable some voxel finders in some cases + + // check if any alignment was applied + + bool isAlignmentApplied = false; + { + TObjArray* nodes = gGeoManager->GetListOfPhysicalNodes(); + if (nodes) { + for (int in = 0; in < nodes->GetEntriesFast(); in++) { + TGeoPhysicalNode* node = dynamic_cast<TGeoPhysicalNode*>(nodes->At(in)); + if (!node) continue; + if (node->IsAligned()) { + isAlignmentApplied = true; + break; + } + } + } + } + + // disable potentially problematic voxel finders + + if (isAlignmentApplied) { + TObjArray* volumes = gGeoManager->GetListOfVolumes(); + if (volumes) { + for (int iv = 0; iv < volumes->GetEntriesFast(); iv++) { + TGeoVolumeAssembly* vol = dynamic_cast<TGeoVolumeAssembly*>(volumes->At(iv)); + if (!vol) { + continue; + } + TGeoVoxelFinder* finder = vol->GetVoxels(); + if (finder) { + finder->SetInvalid(); + } + } + } + } + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +MaterialMapCreator::~MaterialMapCreator() +{ + // destructor + + CleanUpThreads(); + + // delete the navigators, created in this class + + for (auto i = fNavigators.begin(); i != fNavigators.end(); i++) { + gGeoManager->RemoveNavigator(*i); + } + + // once TGeoManager is swithched in multithreaded mode, there is no way to make it non-mltithreaded again + // therefore we should set SetMaxThreads( >=0 ) + + if (fNthreadsOld > 0) { + gGeoManager->SetMaxThreads(fNthreadsOld); + } + else { + gGeoManager->SetMaxThreads(1); + } + + // ensure that the default navigator exists + + GetCurrentNavigator(-1); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +TGeoNavigator* MaterialMapCreator::GetCurrentNavigator(int iThread) +{ + // Get navigator for current thread, create it if it doesnt exist. + // Produce an error when anything goes wrong + + TGeoNavigator* navi = gGeoManager->GetCurrentNavigator(); + if (!navi) { + + navi = gGeoManager->AddNavigator(); + + if (iThread >= 0) { + fNavigators.push_back(navi); + } + + if (navi != gGeoManager->GetCurrentNavigator()) { + LOG(fatal) << "ca::tools::MaterialMapCreator: unexpected behavior of TGeoManager::AddNavigator() !!"; + } + } + + if (!navi) { + LOG(fatal) << "ca::tools::MaterialMapCreator: can not find / create TGeoNavigator for thread " << iThread; + } + + return navi; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void MaterialMapCreator::CleanUpThreads() +{ + // clean up thread data in TGeoManager + + gGeoManager->ClearThreadsMap(); + + // create a default navigator for multithreaded mode + // otherwise gGeoManager->GetCurrentNavigator() returns nullptr that might produces crashes in other code + + GetCurrentNavigator(-1); +} + +void MaterialMapCreator::InitThreads() +{ + // (re)initialise the number of threads in TGeoManager + + // reserve enough threads in TGeoManager + if (fNthreads > gGeoManager->GetMaxThreads()) { + gGeoManager->SetMaxThreads(fNthreads); + // in case the number of threads is truncated by TGeoManager (must not happen) + fNthreads = gGeoManager->GetMaxThreads(); + if (fNthreads < 1) { + fNthreads = 1; + } + } + CleanUpThreads(); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +MaterialMap MaterialMapCreator::GenerateMaterialMap(double zRef, double zMin, double zMax, double xyMax, int nBinsDim) +{ + if (fDoRadialProjection) { + if (fTargetZ >= zRef - 0.1) { + LOG(warn) + << "kf::tools::MaterialMapCreator: the material reference z-coordinate must be at least 0.1 cm downstream " + "the target. Shifting the reference z-coordinate to the lower limit: target Z = " + << fTargetZ << ", material reference z = " << zRef; + } + zRef = std::max(zRef, fTargetZ + 1.); + zMin = std::max(fTargetZ, zMin); + } + + if (!(zMin <= zRef && zRef <= zMax)) { + LOG(fatal) << "kf::tools::MaterialMapCreator: the material parameters are inconsistent. It must be: zMin " << zMin + << " <= zRef " << zRef << " <= zMax " << zMax; + } + + MaterialMap matBudget(nBinsDim, xyMax, zRef, zMin, zMax); + double binSizeX = 2. * xyMax / nBinsDim; + double binSizeY = 2. * xyMax / nBinsDim; + + // call it every time. for the case the number of threads was reset in some other code + InitThreads(); + + long nThreadCrosses[fNthreads]; + long nThreadRays[fNthreads]; + long nThreadRaysExpected = nBinsDim * nBinsDim * fNraysBinPerDim * fNraysBinPerDim / fNthreads; + for (int i = 0; i < fNthreads; i++) { + nThreadCrosses[i] = 0; + nThreadRays[i] = 0; + } + + LOG_IF(info, fVerbose > 1) << "kf::tools::MaterialMapCreator: Generate material map using " << fNthreads + << " threads.."; + + auto naviThread = [&](int iThread) { + // create a navigator for this thread + TGeoNavigator* navi = GetCurrentNavigator(iThread); + + for (int iBinX = iThread; iBinX < nBinsDim; iBinX += fNthreads) { + for (int iBinY = 0; iBinY < nBinsDim; ++iBinY) { + double radThick = 0.; + int nRays = 0; + double d = 1. / (fNraysBinPerDim); + for (int iStepX = 0; iStepX < fNraysBinPerDim; iStepX++) { + for (int iStepY = 0; iStepY < fNraysBinPerDim; iStepY++) { + + // ray position at zRef + double rayX = -xyMax + (iBinX + d * (iStepX + 0.5)) * binSizeX; + double rayY = -xyMax + (iBinY + d * (iStepY + 0.5)) * binSizeY; + + // ray slopes + double tx = 0.; + double ty = 0.; + double t = 1.; + if (fDoRadialProjection) { + tx = rayX / (zRef - fTargetZ); + ty = rayY / (zRef - fTargetZ); + t = sqrt(1. + tx * tx + ty * ty); + } + + // ray position at zMin + double z = zMin; + double x = rayX + tx * (z - zRef); + double y = rayY + ty * (z - zRef); + LOG_IF(info, fVerbose > 2) << "ray at " << x << " " << y << " " << z << " zMin " << zMin << " zMax " + << zMax; + + TGeoNode* node = navi->InitTrack(x, y, z, tx / t, ty / t, 1. / t); + + bool doContinue = 1; + for (int iStep = 0; doContinue; iStep++) { + + if (!node) { + // may happen when tracing outside of the CBM volume -> produce a warning + LOG(warning) << "kf::tools::MaterialMapCreator: TGeoNavigator can not find the geo node"; + break; + } + + TGeoMedium* medium = node->GetMedium(); + if (!medium) { + LOG(fatal) << "kf::tools::MaterialMapCreator: TGeoNavigator can not find the geo medium"; + } + + TGeoMaterial* material = medium->GetMaterial(); + if (!material) { + LOG(fatal) << "kf::tools::MaterialMapCreator: TGeoNavigator can not find the geo material"; + } + + double radLen = material->GetRadLen(); + + if (radLen < kMinRadLength) { // 0.5612 is rad. length of Lead at normal density + LOG(fatal) << "kf::tools::MaterialMapCreator: failed assertion! " + << " TGeoNavigator returns a suspicious material with an unrealistic " + << "radiation length of " << radLen << " cm. " + << " The allowed minimum is 0.56 cm (Lead has 0.5612 cm). Check your material! " + "Modify this assertion if necessary." + << " TGeoNode \"" << node->GetName() << "\", TGeoMedium \"" << medium->GetName() + << "\", TGeoMaterial \"" << material->GetName() << "\""; + } + + // move navi to the next material border + node = navi->FindNextBoundaryAndStep(); //5., kTRUE); + nThreadCrosses[iThread]++; + + LOG_IF(info, fVerbose > 2) << " RL " << radLen << " next pt " << navi->GetCurrentPoint()[0] << " " + << navi->GetCurrentPoint()[1] << " " << navi->GetCurrentPoint()[2]; + + double zNew = navi->GetCurrentPoint()[2]; + if (zNew >= zMax) { + zNew = zMax; + doContinue = 0; + } + + //if (zNew < z) { + //LOG(info) << " z curr " << z << " z new " << zNew << " dz " << zNew - z ; } + double dz = zNew - z; + if (dz < 0.) { + if (iStep == 0 && dz > -1.e-8) { + // when the ray starts exactly at the volume border, first dz might become negative, + // probably due to some roundoff errors. So we let it be a bit negative for the first intersection. + dz = 0.; + } + else { + LOG(fatal) + << "kf::tools::MaterialMapCreator: TGeoNavigator propagates the ray upstream. Something is wrong." + << " z old " << z << " z new " << zNew << ", dz " << dz; + } + } + radThick += dz / radLen; + z = zNew; + } + + nRays++; + nThreadRays[iThread]++; + if (fVerbose > 2 && nThreadRays[iThread] % 1000000 == 0) { + LOG(info) << "kf::tools::MaterialMapCreator: report from thread " << iThread << ": material map is " + << 100. * nThreadRays[iThread] / nThreadRaysExpected << " \% done"; + } + } + } + radThick /= nRays; + LOG_IF(info, fVerbose > 2) << " radThick " << radThick; + //doPrint = (radThick > 0.01); + matBudget.SetRadThickBin(iBinX, iBinY, radThick); + } // iBinX + } // iBinY + }; + + std::vector<std::thread> threads(fNthreads); + + // run n threads + for (int i = 0; i < fNthreads; i++) { + threads[i] = std::thread(naviThread, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + + CleanUpThreads(); + long nRays = 0; + long nCrosses = 0; + for (int i = 0; i < fNthreads; i++) { + nRays += nThreadRays[i]; + nCrosses += nThreadCrosses[i]; + } + + LOG_IF(info, fVerbose > 0) << "kf::tools::MaterialMapCreator: shooted " << nRays << " rays. Each ray crossed " + << nCrosses * 1. / nRays << " material boundaries in average"; + + return matBudget; +} diff --git a/algo/kf/tools/KfMaterialMapCreator.h b/algo/kf/tools/KfMaterialMapCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..4814228e60328e7c59d11b975496450740378ac0 --- /dev/null +++ b/algo/kf/tools/KfMaterialMapCreator.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file KfMaterialMapCreator.h +/// \brief Utility to generate material budget map from the TGeoNavigator representation of the Setup (implementation) +/// \author Sergey Gorbunov <se.gorbunov@gsi.de> +/// \author Sergei Zharko <s.zharko@gsi.de> +/// \date 29.08.2024 + +#pragma once + +#include "Rtypes.h" +#include "TObject.h" + +class TGeoNavigator; +namespace cbm::algo::kf +{ + class MaterialMap; +} + +namespace kf::tools +{ + /// \class MaterialMapCreator + /// \brief An utility class to create a material budget map from the TGeo + class MaterialMapCreator { + public: + /// \brief Constructor from parameters + /// \param verbose Verbosity level (0 - only warning/error output) + MaterialMapCreator(int verbose = 0); + + /// \brief Destructor + ~MaterialMapCreator(); + + /// \brief Project rays radially from the targetZ througth the XY-bins at a reference z. + /// \param targetZ z-coordinate of the target [cm] + void SetDoRadialProjection(double targetZ) + { + fDoRadialProjection = true; + fTargetZ = targetZ; + } + + /// \brief Project rays horisontally along the Z axis (default) + void SetDoHorisontalProjection() { fDoRadialProjection = false; } + + /// \brief Shoots nRaysDim * nRaysDim rays for each bin in the map + void SetNraysPerDim(int nRaysDim) { fNraysBinPerDim = (nRaysDim > 0) ? nRaysDim : 1; } + + + /// \brief Generates a material budget map + /// \param zRef Reference z-coordinate of the material layer [cm] + /// \param zMin z-coordinate of the material layer lower boundary [cm] + /// \param zMax z-coordinate of the material layer upper boundary [cm] + /// \param xyMax Transverse size of the material layer [cm] + /// \param nBinsDim Number of bins in the x(y) axis + /// + /// generate a material map, collecting the material between [zMin, zMax] + /// with radial rays from (0,0,targetZ) through the XY-bins at z == zRef. + /// + /// It creates a map with [nBinsDim x nBinsDim] bins, of a size of [+-xyMax, +-xyMax] + /// shooting fNraysBinPerDim x fNraysBinPerDim through each bin + /// + /// The calculated radiation thickness is a projection of the rad.thickness along the ray onto the Z axis. + /// RadThick = sum of (dZ / radLength) over ray trajectory pieces + /// + /// When doRadialProjection==false the rays are shoot horizontally along the Z axis + /// + cbm::algo::kf::MaterialMap GenerateMaterialMap(double zRef, double zMin, double zMax, double xyMax, int nBinsDim); + + /// \brief Enables safe mode of the material initialization + void SetSafeMaterialInitialization(bool val = true) { fDoSafeInitialization = val; } + + private: + /// \brief Initializes the necessary amount of threads in TGeoManager + void InitThreads(); + + /// \brief Cleans up the TGeoManager: threadIds, create a default navigator + void CleanUpThreads(); + + /// \brief Gets navigator for current thread, creates it if it does not exist + /// \param iThread Index of the thread + /// \throw std::runtime_error If the TGeoNavigator is not found + TGeoNavigator* GetCurrentNavigator(int iThread); + + private: + static constexpr double kMinRadLength = 0.3; ///< Minimal radiational length allowed [cm] + + std::vector<TGeoNavigator*> fNavigators{}; ///< list of created navigators + double fTargetZ{0.}; ///< z of the target for the radial projection + int fNthreadsOld{0}; ///< number of threads in TGeoManager before the helper was created + int fNthreads{0}; ///< number of threads + int fNraysBinPerDim{3}; ///< shoot fNraysBinPerDim * fNraysBinPerDim rays in each map bin + int fVerbose{0}; ///< verbosity level + bool fDoRadialProjection{false}; ///< if project rays horizontally along the Z axis (special mode) + bool fDoSafeInitialization{false}; ///< performs slow but safe initialization + ///< to get around the crashes in TGeoVoxelFinder + }; + +} // namespace kf::tools diff --git a/algo/kf/tools/README.md b/algo/kf/tools/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0d02858a4918eb687e43d43c9b24bfd00531bf0c --- /dev/null +++ b/algo/kf/tools/README.md @@ -0,0 +1,10 @@ +# KfTools Library + +## Introduction + +The KfTools contains additional utilities for the KF-framework, which cannot be included into +the cbm::algo namespace, but in the same time are experiment-independent. + +## Library contents + +### **Material map generator** diff --git a/reco/L1/CMakeLists.txt b/reco/L1/CMakeLists.txt index f2cdc2a69df550c0461db31a41a437ced696ca0a..9b02fa975f02e4d404c1577c5c1e46539e8e5ecc 100644 --- a/reco/L1/CMakeLists.txt +++ b/reco/L1/CMakeLists.txt @@ -168,6 +168,7 @@ set(PRIVATE_DEPENDENCIES CbmRecoSts CbmSimSteer CbmRecoBase + KfCbm KFParticle external::yaml-cpp FairRoot::GeoBase diff --git a/reco/L1/CbmL1.cxx b/reco/L1/CbmL1.cxx index 203dd07412599892a7f7a4afd1f02c360013a029..91a5616ade754a5a71d974bc880970e053b37bd3 100644 --- a/reco/L1/CbmL1.cxx +++ b/reco/L1/CbmL1.cxx @@ -37,6 +37,7 @@ #include "CaToolsDebugger.h" #include "CaToolsField.h" #include "CbmEvent.h" +#include "CbmKfTrackingSetupInitializer.h" #include "CbmMCDataObject.h" #include "CbmStsFindTracks.h" #include "CbmStsHit.h" @@ -72,6 +73,7 @@ using cbm::algo::ca::Parameters; using cbm::ca::MCModule; using cbm::ca::TimeSliceReader; using cbm::ca::tools::MaterialHelper; +using cbm::kf::TrackingSetupInitializer; ClassImp(CbmL1); @@ -239,6 +241,7 @@ try { CheckDetectorPresence(); + // ***************************** // ** ** // ** GEOMETRY INITIALIZATION ** @@ -547,6 +550,81 @@ try { } fpAlgo->Init(fTrackingMode); + if constexpr (0) { + // **** FEATURE: KF-SETUP INITIALIZATION ***** + // Creating a setup + auto pSetupInitializer = std::make_unique<TrackingSetupInitializer>(); + pSetupInitializer->Use(fUseMVD, fUseSTS, fUseMUCH, fUseTRD, fUseTOF); + pSetupInitializer->InitProperties(); + auto trackerSetup = pSetupInitializer->MakeSetup<double>(/*provide original field*/ true); + LOG(info) << "!!!!\n!!!!\n!!!!\n!!!! KF SETUP:\n" << trackerSetup.ToString(0) << "\n!!!!\n!!!!\n!!!!"; + + // Storing setup + TrackingSetupInitializer::Store(trackerSetup, "./trackerSetup.geo.kf.bin"); + + // Reading setup (now in fvec): + auto trackerSetupVec = TrackingSetupInitializer::Load<fvec>("./trackerSetup.geo.kf.bin"); + LOG(info) << "!!!!\n!!!!\n!!!!\n!!!! KF SETUP (from file):\n" + << trackerSetupVec.ToString(1) << "\n!!!!\n!!!!\n!!!!"; + + + { + // TEST: magnetic field + const auto& fld = trackerSetupVec.GetField<cbm::algo::kf::EFieldMode::Intrpl>(); + struct Node { + int iSt; + fvec x; + fvec y; + }; + { + Node n{.iSt = 1, .x = 2.3, .y = 1.2}; + auto kfFldValue = fld.GetFieldValue(n.iSt, n.x, n.y); + auto caFldValue = fpParameters->GetStation(n.iSt).fieldSlice.GetFieldValue(n.x, n.y); + LOG(info) << "A: " << kfFldValue.ToString() << " --- " << caFldValue.ToString(); + } + { + Node n{.iSt = 2, .x = -2.1, .y = 2.2}; + auto kfFldValue = fld.GetFieldValue(n.iSt, n.x, n.y); + auto caFldValue = fpParameters->GetStation(n.iSt).fieldSlice.GetFieldValue(n.x, n.y); + LOG(info) << "B: " << kfFldValue.ToString() << " --- " << caFldValue.ToString(); + } + { + Node n{.iSt = 5, .x = -2.1, .y = 2.2}; + auto kfFldValue = fld.GetFieldValue(n.iSt, n.x, n.y); + auto caFldValue = fpParameters->GetStation(n.iSt).fieldSlice.GetFieldValue(n.x, n.y); + LOG(info) << "B: " << kfFldValue.ToString() << " --- " << caFldValue.ToString(); + } + + // TEST: material maps + { + Node n{.iSt = 1, .x = 2.3, .y = 1.2}; + LOG(info) << trackerSetupVec.GetMaterial(n.iSt).GetThickness(n.x, n.y) << " -- " + << fpParameters->GetMaterialThickness(n.iSt, n.x, n.y); + } + { + Node n{.iSt = 1, .x = -2.3, .y = -1.2}; + LOG(info) << trackerSetupVec.GetMaterial(n.iSt).GetThickness(n.x, n.y) << " -- " + << fpParameters->GetMaterialThickness(n.iSt, n.x, n.y); + } + { + Node n{.iSt = 1, .x = 2.5, .y = 0.2}; + LOG(info) << trackerSetupVec.GetMaterial(n.iSt).GetThickness(n.x, n.y) << " -- " + << fpParameters->GetMaterialThickness(n.iSt, n.x, n.y); + } + { + Node n{.iSt = 4, .x = 5.3, .y = -2.2}; + LOG(info) << trackerSetupVec.GetMaterial(n.iSt).GetThickness(n.x, n.y) << " -- " + << fpParameters->GetMaterialThickness(n.iSt, n.x, n.y); + } + { + Node n{.iSt = 4, .x = 0, .y = -1.2}; + LOG(info) << trackerSetupVec.GetMaterial(n.iSt).GetThickness(n.x, n.y) << " -- " + << fpParameters->GetMaterialThickness(n.iSt, n.x, n.y); + } + } + } + + // Initialize time-slice reader fpTSReader = std::make_unique<TimeSliceReader>(); fpTSReader->SetDetector(ca::EDetectorID::kMvd, fUseMVD); diff --git a/reco/kf/CMakeLists.txt b/reco/kf/CMakeLists.txt index 9a5e8b40448fcddabfdd3c1ebce03f8acd373d32..ff911e51c2e38dad008f201720ebde1e36e9ff53 100644 --- a/reco/kf/CMakeLists.txt +++ b/reco/kf/CMakeLists.txt @@ -16,17 +16,17 @@ Else() ADD_DEFINITIONS(-Wall -Wextra -Wsign-promo -Wno-pmf-conversions -Wctor-dtor-privacy -Wreorder -Wno-deprecated -Wstrict-null-sentinel -Wno-non-template-friend -Wno-parentheses -Wmissing-field-initializers) # -Weffc++ -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast : wait for other parts of cbmroot\root. EndIf() -add_library(CbmKf SHARED ${SRCS}) +add_library(KfCbm SHARED ${SRCS}) #list(APPEND HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/utils/KfSimd.h) -target_include_directories(CbmKf +target_include_directories(KfCbm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) #target_compile_definitions(KfCore PUBLIC NO_ROOT) -target_link_libraries(CbmKf +target_link_libraries(KfCbm PUBLIC CbmMuchBase CbmMvdBase CbmStsBase @@ -59,7 +59,7 @@ target_link_libraries(CbmKf ROOT::RIO ) -install(TARGETS CbmKf DESTINATION lib) +install(TARGETS KfCbm DESTINATION lib) #install(DIRECTORY kf TYPE INCLUDE FILES_MATCHING PATTERN "*.h") #install(DIRECTORY kf/utils TYPE INCLUDE FILES_MATCHING PATTERN "*.h") #install(DIRECTORY kf/data TYPE INCLUDE FILES_MATCHING PATTERN "*.h") @@ -69,6 +69,7 @@ install(TARGETS CbmKf DESTINATION lib) install( FILES + CbmKfOriginalField.h CbmKfTarget.h CbmKfTrackingSetupInitializer.h DESTINATION diff --git a/reco/kf/CbmKfOriginalField.h b/reco/kf/CbmKfOriginalField.h new file mode 100644 index 0000000000000000000000000000000000000000..f87a18c1cf551cc476362285f581c57ee2ad4ff0 --- /dev/null +++ b/reco/kf/CbmKfOriginalField.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergey Gorbunov[committer] */ + +/// \file CbmKfOriginalField.h +/// \brief Thread-safe representation of the magnetic field (header) +/// \since 17.10.2023 +/// \author S.Gorbunov + +#pragma once // include this header only once per compilation unit + +#include "FairField.h" +#include "FairRunAna.h" + +#include <mutex> +#include <tuple> + +namespace cbm::kf +{ + /// \class OriginalField + /// \brief Thread-safe representation of the magnetic field in CBM + class OriginalField { + public: + /// \brief Returns magnetic field flux density in a spatial point + /// \param x x-coordinate of the point [cm] + /// \param y y-coordinate of the point [cm] + /// \param z z-coordinate of the point [cm] + /// \return tuple(Bx, By, Bz) of the magnetic field flux density components [kG] + std::tuple<double, double, double> operator()(double x, double y, double z) const + { + assert(FairRunAna::Instance()); + assert(FairRunAna::Instance()->GetField()); + double pos[3] = {x, y, z}; + double B[3] = {0., 0., 0.}; + // protect the field access + // TODO: make CbmField thread-safe + static std::mutex mymutex; + mymutex.lock(); + FairRunAna::Instance()->GetField()->GetFieldValue(pos, B); + mymutex.unlock(); + return std::tuple(B[0], B[1], B[2]); + } + }; + + /// \class ZeroField + class ZeroField { + public: + /// \brief Returns magnetic field flux density in a spatial point + /// \param x x-coordinate of the point [cm] + /// \param y y-coordinate of the point [cm] + /// \param z z-coordinate of the point [cm] + /// \return tuple(Bx, By, Bz) of the magnetic field flux density components [kG] + std::tuple<double, double, double> operator()(double, double, double) const { return std::tuple(0., 0., 0.); } + }; +} // namespace cbm::kf diff --git a/reco/kf/CbmKfTarget.cxx b/reco/kf/CbmKfTarget.cxx index a572edec2a9abc90a5807ed8c76c1b6a2e7c44b1..431e91d46aad433d316a754b234580e097f53f39 100644 --- a/reco/kf/CbmKfTarget.cxx +++ b/reco/kf/CbmKfTarget.cxx @@ -38,36 +38,39 @@ Target* Target::Instance() // --------------------------------------------------------------------------------------------------------------------- // -void Target::Init() +void Target::FindTargetNode(TString& targetPath, TGeoNode*& targetNode) { - if (!gGeoManager) { - throw std::logic_error("cbm::kf::Target: the TGeoManager instance is not initialized on this step. Please be " - "ensured to call the Target::Init() after the TGeoManager initialization."); + if (!targetNode) { // init at the top of the tree + targetNode = gGeoManager->GetTopNode(); + targetPath = "/" + TString(targetNode->GetName()); } - std::function<void(TString&, TGeoNode*)> FindTargetNode = [&](TString& targetPath, TGeoNode* targetNode) -> void { - if (!targetNode) { // init at the top of the tree - targetNode = gGeoManager->GetTopNode(); - targetPath = "/" + TString(targetNode->GetName()); - } + if (TString(targetNode->GetName()).Contains("target")) { + return; + } - if (TString(targetNode->GetName()).Contains("target")) { + for (Int_t iNode = 0; iNode < targetNode->GetNdaughters(); ++iNode) { + TGeoNode* newNode = targetNode->GetDaughter(iNode); + TString newPath = targetPath + "/" + newNode->GetName(); + FindTargetNode(newPath, newNode); + if (newNode) { + targetPath = newPath; + targetNode = newNode; return; } + } + targetPath = ""; + targetNode = nullptr; +} - for (Int_t iNode = 0; iNode < targetNode->GetNdaughters(); ++iNode) { - TGeoNode* newNode = targetNode->GetDaughter(iNode); - TString newPath = targetPath + "/" + newNode->GetName(); - FindTargetNode(newPath, newNode); - if (newNode) { - targetPath = newPath; - targetNode = newNode; - return; - } - } - targetPath = ""; - targetNode = nullptr; - }; +// --------------------------------------------------------------------------------------------------------------------- +// +void Target::Init() +{ + if (!gGeoManager) { + throw std::logic_error("cbm::kf::Target: the TGeoManager instance is not initialized on this step. Please be " + "ensured to call the Target::Init() after the TGeoManager initialization."); + } TString targetPath; TGeoNode* pTargetNode{nullptr}; diff --git a/reco/kf/CbmKfTarget.h b/reco/kf/CbmKfTarget.h index 2440ad513e264b7b2bbd25075174e40661e65ad8..f9106f4c2c9af3651539ebe2ce57925f21827359 100644 --- a/reco/kf/CbmKfTarget.h +++ b/reco/kf/CbmKfTarget.h @@ -15,6 +15,9 @@ #include <mutex> +class TString; +class TGeoNode; + namespace cbm::kf { /// \class Target @@ -55,6 +58,9 @@ namespace cbm::kf /// \brief Target initializer void Init(); + /// \brief Finds a target + static void FindTargetNode(TString& targetPath, TGeoNode*& targetNode); + double fX{cbm::algo::kf::defs::Undef<double>}; ///< reference x-coordinate of the target position [cm] double fY{cbm::algo::kf::defs::Undef<double>}; ///< reference y-coordinate of the target position [cm] double fZ{cbm::algo::kf::defs::Undef<double>}; ///< reference z-coordinate of the target position [cm] diff --git a/reco/kf/CbmKfTrackingSetupInitializer.cxx b/reco/kf/CbmKfTrackingSetupInitializer.cxx index 4a418301bca9f8fe4d37442764546ae914f4a346..9650ac15884b69c2976eda0f5f5553d516acaa75 100644 --- a/reco/kf/CbmKfTrackingSetupInitializer.cxx +++ b/reco/kf/CbmKfTrackingSetupInitializer.cxx @@ -9,6 +9,7 @@ #include "CbmKfTrackingSetupInitializer.h" +#include "CbmKfOriginalField.h" #include "CbmKfTarget.h" #include "CbmMuchTrackingInterface.h" #include "CbmMvdTrackingInterface.h" @@ -20,6 +21,7 @@ #include "FairRunAna.h" #include "KfDefs.h" #include "KfMaterialMapCreator.h" +#include "Logger.h" #include <functional> @@ -41,12 +43,11 @@ try { // Magnetic field initialization if (FairRunAna::Instance()->GetField()) { - this->SetFieldFunction( - [](const double(&r)[3], double* B) { FairRunAna::Instance()->GetField()->GetFieldValue(r, B); }, - EFieldType::Normal); + this->SetFieldFunction(OriginalField(), EFieldType::Normal); } else { - this->SetFieldFunction(cbm::algo::kf::defs::ZeroFieldFn, EFieldType::Null); + //this->SetFieldFunction(cbm::algo::kf::defs::ZeroFieldFn, EFieldType::Null); + this->SetFieldFunction(ZeroField(), EFieldType::Null); } // Tracking station property initialization @@ -98,13 +99,18 @@ try { double xyMax{1.3 * pTarget->GetRmax()}; int nBins{std::min(static_cast<int>(std::ceil(2. * xyMax / kMatCreatorPitch)), kMatCreatorMaxNbins)}; assert(nBins > 0); - double zFirst{pTarget->GetZ() + pTarget->GetDz()}; // z-coordinate of the target material lower limit + double zFirst{pTarget->GetZ() - pTarget->GetDz()}; // z-coordinate of the target material lower limit LOG(info) << "- collecting target material (z = " << zFirst << " - " << zLast << ", size = " << xyMax << ")\n"; this->SetTargetProperties(pTarget->GetX(), pTarget->GetY(), pTarget->GetZ(), kTargFieldInitStep, materialCreator.GenerateMaterialMap(pTarget->GetZ(), zFirst, zLast, xyMax, nBins)); } // Material and field layers initialization + + for (const auto& layer : geoStations) { + LOG(info) << " >>>>> LAYER >>>>> " << layer.zRef; + } + for (auto layerIt = geoStations.begin(); layerIt != geoStations.end(); ++layerIt) { double z1 = layerIt->zMax; double z2 = z1; @@ -121,7 +127,55 @@ try { this->AddFieldSliceReference(layerIt->xMax, layerIt->yMax, layerIt->zRef); zLast = zNew; } + + if constexpr (0) { + // Estimate acceptance + std::vector<cbm::algo::kf::MaterialMap> mapsTest; + mapsTest.reserve(geoStations.size()); + double slope{0.}; + zLast = + pTarget->GetZ() + pTarget->GetDz() + kTargMaterialOffset; // z-coordinate of the target material upper limit + for (auto layerIt = geoStations.begin(); layerIt != geoStations.end(); ++layerIt) { + double xyMax = 1.3 * std::max(std::fabs(layerIt->xMax), std::fabs(layerIt->yMax)); + slope = std::max(slope, xyMax / (layerIt->zRef - pTarget->GetZ())); + } + for (auto layerIt = geoStations.begin(); layerIt != geoStations.end(); ++layerIt) { + double z1 = layerIt->zMax; + double z2 = z1; + if (std::next(layerIt) != geoStations.end()) { + z2 = std::next(layerIt)->zMin; + } + double zNew = 0.5 * (z1 + z2); + double xyMax = slope * (layerIt->zRef - pTarget->GetZ()); + //int nBins = std::min(static_cast<int>(std::ceil(2. * xyMax / kMatCreatorPitch)), kMatCreatorMaxNbins); + int nBins = 100; + assert(nBins > 0); + + LOG(info) << "* collecting material between z = " << zLast << " and " << zNew << " [cm]"; + mapsTest.push_back(materialCreator.GenerateMaterialMap(layerIt->zRef, zLast, zNew, xyMax, nBins)); + zLast = zNew; + } + for (const auto& map : mapsTest) { + LOG(info) << "TEST: " << map.ToString(0, 2); + } + { + // Addition test + int iInactive{2}; + auto& M1{mapsTest[iInactive]}; + auto& M2{mapsTest[iInactive + 1]}; + auto M12gen{ + materialCreator.GenerateMaterialMap(M2.GetZref(), M1.GetZmin(), M2.GetZmax(), M2.GetXYmax(), M2.GetNbins())}; + auto M12add{M2}; + M12add.Add(M1); + LOG(info) << "MATERIAL MAP ADDITION TEST:"; + LOG(info) << "M1=\n" << M1.ToString(0, 2); + LOG(info) << "M2=\n" << M2.ToString(0, 2); + LOG(info) << "M12gen=\n" << M2.ToString(0, 2); + LOG(info) << "M12add=\n" << M2.ToString(0, 2); + } + } } + LOG(info) << "kf::TrackingSetupInitializer: Tracking setup was initialized successfully"; return true; } catch (const std::exception& err) { diff --git a/reco/kf/CbmKfTrackingSetupInitializer.h b/reco/kf/CbmKfTrackingSetupInitializer.h index 4148c1ff636c7410c302217dbf94e6a21fb17185..50e643bb1589b47647162b80d4acb0443edfb221 100644 --- a/reco/kf/CbmKfTrackingSetupInitializer.h +++ b/reco/kf/CbmKfTrackingSetupInitializer.h @@ -9,7 +9,7 @@ #pragma once -#include "KfSetup.h" +#include "KfSetupInitializer.h" #include <tuple> @@ -37,12 +37,12 @@ namespace cbm::kf private: // Material map creator properties (TODO: Provide setters, if needed) - static constexpr double kMatCreatorPitch{0.1}; ///< Material budget map minimal bin size [cm] - static constexpr int kMatCreatorMaxNbins{100}; ///< Max number of bins in the material budget map in x(y) axis - static constexpr int kMatCreatorNrays{3}; ///< Number of rays per dimension for the material budget - static constexpr bool kMatCreatorSafeMode{true}; ///< Safe mode of the material map creation - static constexpr double kTargFieldInitStep{2.5}; ///< Step between nodes in the target field initialization [cm] - static constexpr double kTargMaterialOffset{0.2}; ///< Offset between target upper limit and its material zMax [cm] + static constexpr double kMatCreatorPitch{0.1}; ///< Material budget map minimal bin size [cm] + static constexpr int kMatCreatorMaxNbins{100}; ///< Max number of bins in the material budget map in x(y) axis + static constexpr int kMatCreatorNrays{3}; ///< Number of rays per dimension for the material budget + static constexpr bool kMatCreatorSafeMode{true}; ///< Safe mode of the material map creation + static constexpr double kTargFieldInitStep{2.5}; ///< Step between nodes in the target field initialization [cm] + static constexpr double kTargMaterialOffset{1}; ///< Offset between target upper limit and its material zMax [cm] bool fbUseMvd{false}; ///< Are MVD stations included in the tracking setup bool fbUseSts{false}; ///< Are STS stations included in the tracking setup