diff --git a/Core/include/Acts/Utilities/Helpers.hpp b/Core/include/Acts/Utilities/Helpers.hpp index e37fc8b032492bea69f473a97422a87625e97223..5bc4df0cae94e6cc1e30f5da4678bfd494b37419 100644 --- a/Core/include/Acts/Utilities/Helpers.hpp +++ b/Core/include/Acts/Utilities/Helpers.hpp @@ -14,6 +14,7 @@ // libc/STL include(s) #include <cmath> +#include <cstdlib> #include <iomanip> #include <iostream> #include <memory> @@ -65,50 +66,133 @@ namespace VectorHelpers { } // namespace detail - // default call on Eigen types, calculate radius + /// Calculate phi (transverse plane angle) from compatible Eigen types + /// @tparam Derived Eigen derived concrete type + /// @param v Any vector like Eigen type, static or dynamic + /// @note Will static assert that the number of rows of @p v is at least 2, or + /// in case of dynamic size, will abort execution if that is not the case. + /// @return The value of the angle in the transverse plane. template <typename Derived> double - phi(const Eigen::MatrixBase<Derived>& v) + phi(const Eigen::MatrixBase<Derived>& v) noexcept { - if (v.rows() < 2) { - return 0.; + constexpr int rows = Eigen::MatrixBase<Derived>::RowsAtCompileTime; + if + constexpr(rows != -1) + { + // static size, do compile time check + static_assert(rows >= 2, + "Phi function not valid for vectors not at least 2D"); + } + else { + // dynamic size + if (v.rows() < 2) { + std::cerr << "Phi function not valid for vectors not at least 2D" + << std::endl; + std::abort(); + } } + return std::atan2(v[1], v[0]); } - // if called-upon type has phi method, call that + /// Calculate phi (transverse plane angle) from anything implementing a method + /// like `phi()` returing anything convertible to `double`. + /// @tparam T anything that has a phi method + /// @param v Any type that implements a phi method + /// @return The phi value template <typename T, std::enable_if_t<detail::has_phi_method<T>::value, int> = 0> double - phi(const T& v) + phi(const T& v) noexcept { return v.phi(); } + /// Calculate radius in the transverse (xy) plane of a vector + /// @tparam Derived Eigen derived concrete type + /// @param v Any vector like Eigen type, static or dynamic + /// @note Will static assert that the number of rows of @p v is at least 2, or + /// in case of dynamic size, will abort execution if that is not the case. + /// @return The transverse radius value. template <typename Derived> double - perp(const Eigen::MatrixBase<Derived>& v) + perp(const Eigen::MatrixBase<Derived>& v) noexcept { - if (v.rows() < 2) { - return 0.; + constexpr int rows = Eigen::MatrixBase<Derived>::RowsAtCompileTime; + if + constexpr(rows != -1) + { + // static size, do compile time check + static_assert(rows >= 2, + "Perp function not valid for vectors not at least 2D"); + } + else { + // dynamic size + if (v.rows() < 2) { + std::cerr << "Perp function not valid for vectors not at least 2D" + << std::endl; + std::abort(); + } } return std::sqrt(v[0] * v[0] + v[1] * v[1]); } + /// Calculate the theta angle (longitudinal w.r.t. z axis) of a vector + /// @tparam Derived Eigen derived concrete type + /// @param v Any vector like Eigen type, static or dynamic + /// @note Will static assert that the number of rows of @p v is at least 3, or + /// in case of dynamic size, will abort execution if that is not the case. + /// @return The theta value template <typename Derived> double - theta(const Eigen::MatrixBase<Derived>& v) + theta(const Eigen::MatrixBase<Derived>& v) noexcept { - if (v.rows() < 3) { - return 0.; + constexpr int rows = Eigen::MatrixBase<Derived>::RowsAtCompileTime; + if + constexpr(rows != -1) + { + // static size, do compile time check + static_assert(rows >= 3, + "Theta function not valid for non-3D vectors."); + } + else { + // dynamic size + if (v.rows() < 3) { + std::cerr << "Theta function not valid for non-3D vectors." + << std::endl; + std::abort(); + } } + return std::atan2(std::sqrt(v[0] * v[0] + v[1] * v[1]), v[2]); } + /// Calculate the pseudorapidity for a vector. + /// @tparam Derived Eigen derived concrete type + /// @param v Any vector like Eigen type, static or dynamic + /// @note Will static assert that the number of rows of @p v is at least 3, or + /// in case of dynamic size, will abort execution if that is not the case. + /// @return The pseudorapidity value template <typename Derived> double - eta(const Eigen::MatrixBase<Derived>& v) + eta(const Eigen::MatrixBase<Derived>& v) noexcept { + constexpr int rows = Eigen::MatrixBase<Derived>::RowsAtCompileTime; + if + constexpr(rows != -1) + { + // static size, do compile time check + static_assert(rows >= 3, "Eta function not valid for non-3D vectors."); + } + else { + // dynamic size + if (v.rows() < 3) { + std::cerr << "Eta function not valid for non-3D vectors." << std::endl; + std::abort(); + } + } + return std::atanh(v[2] / v.norm()); } @@ -142,6 +226,11 @@ namespace detail { } } +/// Print out a matrix in a structured way. +/// @param matrix The matrix to print +/// @param precision Numeric output precision +/// @param offset Offset in front of matrix lines +/// @return The printed string inline std::string toString(const ActsMatrixXd& matrix, int precision = 4, @@ -185,6 +274,10 @@ toString(const ActsMatrixXd& matrix, return sout.str(); } +/// Print out a translation in a structured way. +/// @param matrix The translation to print +/// @param precision Numeric output precision +/// @return The printed string inline std::string toString(const Acts::Translation3D& translation, int precision = 4) { @@ -195,6 +288,11 @@ toString(const Acts::Translation3D& translation, int precision = 4) return toString(trans, precision); } +/// Print out a transform in a structured way. +/// @param matrix The transform to print +/// @param precision Numeric output precision +/// @param offset Offset in front of matrix lines +/// @return The printed string inline std::string toString(const Acts::Transform3D& transform, int precision = 4, @@ -209,6 +307,11 @@ toString(const Acts::Transform3D& transform, return sout.str(); } +/// Helper function to unpack a vector of @c shared_ptr into a vector of raw +/// pointers +/// @tparam T the stored type +/// @param items The vector of @c shared_ptr +/// @return The unpacked vector template <typename T> std::vector<T*> unpack_shared_vector(const std::vector<std::shared_ptr<T>>& items) @@ -221,6 +324,11 @@ unpack_shared_vector(const std::vector<std::shared_ptr<T>>& items) return rawPtrs; } +/// Helper function to unpack a vector of @c shared_ptr into a vector of raw +/// pointers (const version) +/// @tparam T the stored type +/// @param items The vector of @c shared_ptr +/// @return The unpacked vector template <typename T> std::vector<const T*> unpack_shared_vector(const std::vector<std::shared_ptr<const T>>& items) diff --git a/Tests/Core/Utilities/CMakeLists.txt b/Tests/Core/Utilities/CMakeLists.txt index bdf9024a8b7f43c23683f698a22c736763ef86d8..a140dd4098525dc2fa7668a3da3e48585c066091 100644 --- a/Tests/Core/Utilities/CMakeLists.txt +++ b/Tests/Core/Utilities/CMakeLists.txt @@ -79,4 +79,8 @@ add_test (NAME VisualizationTests COMMAND VisualizationTests) add_executable (MaterialMapUtilsTests MaterialMapUtilsTests.cpp) target_link_libraries (MaterialMapUtilsTests PRIVATE ActsCore ActsTestsCommonHelpers) add_test (NAME MaterialMapUtilsTest COMMAND MaterialMapUtilsTests) -acts_add_test_to_cdash_project (PROJECT ACore TEST MaterialMapUtilsTest TARGETS MaterialMapUtilsTests) \ No newline at end of file +acts_add_test_to_cdash_project (PROJECT ACore TEST MaterialMapUtilsTest TARGETS MaterialMapUtilsTests) + +add_executable (HelpersTests HelpersTests.cpp) +target_link_libraries (HelpersTests PRIVATE ActsCore ActsTestsCommonHelpers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) +add_test (NAME HelpersTests COMMAND HelpersTests) diff --git a/Tests/Core/Utilities/HelpersTests.cpp b/Tests/Core/Utilities/HelpersTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0ae407b4240a667e57aef76ec53ca8f8eefc9ac --- /dev/null +++ b/Tests/Core/Utilities/HelpersTests.cpp @@ -0,0 +1,157 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 Acts project team +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#define BOOST_TEST_MODULE Helpers Tests +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> + +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Utilities/Helpers.hpp" + +using namespace Acts::VectorHelpers; + +namespace Acts { +namespace Test { + + BOOST_AUTO_TEST_SUITE(Utilities) + + struct MyStruct + { + double + phi() const + { + return 42; + } + }; + + BOOST_AUTO_TEST_CASE(phi_helper_test) + { + Vector3D v(0, 1, 0); + CHECK_CLOSE_ABS(phi(v), M_PI / 2., 1e-9); + + // should work with dynamic types as well + ActsVectorXd v2{3}; + v2 << 0, 1, 0; + CHECK_CLOSE_ABS(phi(v2), M_PI / 2., 1e-9); + + MyStruct s; + BOOST_CHECK_EQUAL(phi(s), 42); + } + + BOOST_AUTO_TEST_CASE(perp_helper_test) + { + Vector3D v(1, 2, 3); + CHECK_CLOSE_ABS(perp(v), std::sqrt(1 + 2 * 2), 1e-9); + + // should work with dynamic types as well + ActsVectorXd v2{3}; + v2 << 1, 2, 3; + CHECK_CLOSE_ABS(perp(v2), std::sqrt(1 + 2 * 2), 1e-9); + } + + BOOST_AUTO_TEST_CASE(theta_eta_test_helper) + { + Vector3D v(1, 2, 3); + CHECK_CLOSE_ABS(theta(v), 0.640522, 1e-5); + CHECK_CLOSE_ABS(eta(v), 1.10359, 1e-5); + + // should work with dynamic types as well + ActsVectorXd v2{3}; + v2 << 1, 2, 3; + CHECK_CLOSE_ABS(theta(v2), 0.640522, 1e-5); + CHECK_CLOSE_ABS(eta(v2), 1.10359, 1e-5); + } + + BOOST_AUTO_TEST_CASE(cross_test_helper) + { + { + Vector3D v(1, 2, 3); + ActsMatrixD<3, 3> mat; + mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; + + ActsMatrixD<3, 3> act = cross(mat, v); + ActsMatrixD<3, 3> exp; + exp << -2, -1, 0, 4, 2, 0, -2, -1, 0; + + CHECK_CLOSE_ABS(act, exp, 1e-9); + } + + // should work with dynamic types as well + { + ActsVectorXd v{3}; + v << 1, 2, 3; + ActsMatrixXd mat{3, 3}; + mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; + + ActsMatrixXd act = cross(mat, v); + ActsMatrixXd exp{3, 3}; + exp << -2, -1, 0, 4, 2, 0, -2, -1, 0; + + BOOST_CHECK(act.isApprox(exp, 1e-9)); + } + } + + BOOST_AUTO_TEST_CASE(toString_test_helper) + { + ActsMatrixD<3, 3> mat; + mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; + std::string out; + out = toString(mat); + BOOST_CHECK(out.size() > 0); + + Translation3D trl{Vector3D{1, 2, 3}}; + out = toString(trl); + BOOST_CHECK(out.size() > 0); + + Transform3D trf; + trf = trl; + out = toString(trf); + BOOST_CHECK(out.size() > 0); + } + + BOOST_AUTO_TEST_CASE(shared_vector_helper_test) + { + { + std::vector<std::shared_ptr<int>> vec; + vec = {std::make_shared<int>(5), + std::make_shared<int>(9), + std::make_shared<int>(26), + std::make_shared<int>(18473)}; + + std::vector<int*> unpacked = unpack_shared_vector(vec); + + std::vector<int*> exp = { + vec[0].get(), vec[1].get(), vec[2].get(), vec[3].get(), + }; + + BOOST_CHECK_EQUAL_COLLECTIONS( + unpacked.begin(), unpacked.end(), exp.begin(), exp.end()); + } + + // same for const + { + std::vector<std::shared_ptr<const int>> vec; + vec = {std::make_shared<const int>(5), + std::make_shared<const int>(9), + std::make_shared<const int>(26), + std::make_shared<const int>(18473)}; + + std::vector<const int*> unpacked = unpack_shared_vector(vec); + + std::vector<const int*> exp = { + vec[0].get(), vec[1].get(), vec[2].get(), vec[3].get(), + }; + + BOOST_CHECK_EQUAL_COLLECTIONS( + unpacked.begin(), unpacked.end(), exp.begin(), exp.end()); + } + } + + BOOST_AUTO_TEST_SUITE_END() +} +}