From 47faeb67daf16b87c680546dc7d6c99341209fd0 Mon Sep 17 00:00:00 2001 From: ralfulrich <ralf.ulrich@kit.edu> Date: Mon, 14 Jan 2019 13:51:02 +0100 Subject: [PATCH] added FourVector with tests --- Framework/Geometry/FourVector.h | 223 +++++++++++++++++++++++++++ Framework/Geometry/testFourVector.cc | 187 ++++++++++++++++++++++ 2 files changed, 410 insertions(+) create mode 100644 Framework/Geometry/FourVector.h create mode 100644 Framework/Geometry/testFourVector.cc diff --git a/Framework/Geometry/FourVector.h b/Framework/Geometry/FourVector.h new file mode 100644 index 000000000..76c5c7ecb --- /dev/null +++ b/Framework/Geometry/FourVector.h @@ -0,0 +1,223 @@ +#ifndef _include_corsika_framework_geometry_fourvector_h_ +#define _include_corsika_framework_geometry_fourvector_h_ + +#include <corsika/geometry/Vector.h> +#include <corsika/units/PhysicalUnits.h> + +#include <iostream> +#include <type_traits> + +namespace corsika::geometry { + +/** + FourVector supports "full" units, e.g. E in [GeV/c] and p in [GeV], + or also t in [s] and r in [m], etc. + + However, for HEP applications it is also possible to use E and p + both in [GeV]. + + The FourVector can return NormSqr and Norm, whereas Norm is + sqrt(abs(NormSqr)). The physical units are always calculated and + returned properly. + + FourVector can also return if it is TimeLike, SpaceLike or PhotonLike. + + When a FourVector is initialized with a lvalue reference, this is + also used for the internal storage, which should lead to complete + disappearance of the FourVector class during optimization. + */ + +template <typename TimeType, typename SpaceVec> class FourVector { + +public: + using SpaceType = typename std::decay<SpaceVec>::type::Quantity; + + /// check the types and the physical units here: + static_assert( + std::is_same<typename std::decay<TimeType>::type, SpaceType>::value || + std::is_same<typename std::decay<TimeType>::type, + decltype(std::declval<SpaceType>() / + corsika::units::si::meter * + corsika::units::si::second)>::value, + "Units of time-like and space-like coordinates must either be idential " + "(e.g. GeV) or [E/c]=[p]"); + +public: + /* + template <typename TT, typename SS> + FourVector(TT && eT, SS && eS) + : fTimeLike(std::forward<TT>(eT)), fSpaceLike(std::forward<SS>(eS)) { + std::cout << "FourVector&&\n"; } + */ + + FourVector(const TimeType &eT, const SpaceVec &eS) + : fTimeLike(eT), fSpaceLike(eS) { + // std::cout << "FourVector const &\n"; + } + + /* + FourVector(TimeType &eT, SpaceVec &eS) + : fTimeLike(eT), fSpaceLike(eS) { + std::cout << "FourVector &\n"; } + */ + + TimeType GetTime() { return fTimeLike; } + + auto GetNormSqr() const { + return GetTimeSquared() - fSpaceLike.squaredNorm(); + } + + SpaceType GetNorm() const { return sqrt(abs(GetNormSqr())); } + + bool IsTimelike() const { + return GetTimeSquared() < fSpaceLike.squaredNorm(); + } // Norm2 < 0 + + bool IsSpacelike() const { + return GetTimeSquared() > fSpaceLike.squaredNorm(); + } // Norm2 > 0 + + bool IsPhotonlike() const { + return GetTimeSquared() == fSpaceLike.squaredNorm(); + } // // Norm2 == 0 + + FourVector &operator+=(const FourVector &b) { + fTimeLike += b.fTimeLike; + fSpaceLike += b.fSpaceLike; + return *this; + } + + FourVector &operator-=(const FourVector &b) { + fTimeLike -= b.fTimeLike; + fSpaceLike -= b.fSpaceLike; + return *this; + } + + FourVector &operator*=(const double b) { + fTimeLike *= b; + fSpaceLike *= b; + return *this; + } + + FourVector &operator/=(const double b) { + fTimeLike /= b; + fSpaceLike.GetComponents() /= b; // TODO: WHY IS THIS?????? + return *this; + } + + FourVector &operator/(const double b) { + *this /= b; + return *this; + } + + /** + Note that the product between two 4-vectors assumes that you use + the same "c" convention for both. Only the LHS vector is checked + for this. You cannot mix different conventions due to + unit-checking. + */ + SpaceType operator*(const FourVector &b) { + if constexpr (std::is_same<typename std::decay<TimeType>::type, + decltype(std::declval<SpaceType>() / + corsika::units::si::meter * + corsika::units::si::second)>::value) + return fTimeLike * b.fTimeLike * + (corsika::units::constants::c * corsika::units::constants::c) - + fSpaceLike.norm(); + else + return fTimeLike * fTimeLike - fSpaceLike.norm(); + } + +private: + /** + This function is automatically compiled to use of ignore the + extra factor of "c" for the time-like quantity + */ + auto GetTimeSquared() const { + if constexpr (std::is_same<typename std::decay<TimeType>::type, + decltype(std::declval<SpaceType>() / + corsika::units::si::meter * + corsika::units::si::second)>::value) + return fTimeLike * fTimeLike * + (corsika::units::constants::c * corsika::units::constants::c); + else + return fTimeLike * fTimeLike; + } + +protected: + /// the data members + TimeType fTimeLike; + SpaceVec fSpaceLike; + + + /// the friends: math operators + template <typename T, typename U> + friend FourVector<T, U> operator+(const FourVector<T, U> &, + const FourVector<T, U> &); + + template <typename T, typename U> + friend FourVector<T, U> operator-(const FourVector<T, U> &, + const FourVector<T, U> &); + + template <typename T, typename U> + friend FourVector<T, U> operator*(const FourVector<T, U> &, const double); + + template <typename T, typename U> + friend FourVector<T, U> operator/(const FourVector<T, U> &, const double); + +}; + +/* +//template<typename T, typename U> FourVector(T& t, U& u) -> +FourVector<decltype(t), decltype(u)>; template<typename T, typename U> +FourVector(const T& t, const U& u) -> FourVector<const typename +std::decay<T>::type, const typename std::decay<U>::type>; template<typename T, +typename U> FourVector(T&& t, U&& u) -> FourVector<typename std::decay<T>::type, +typename std::decay<U>::type>; + // template<typename T, typename U> FourVector(T&& t, U&& u) -> +FourVector<decltype(t), decltype(u)>; + */ + +/** + The math operator+ + */ +template <typename TimeType, typename SpaceVec> +inline FourVector<TimeType, SpaceVec> +operator+(const FourVector<TimeType, SpaceVec> &a, + const FourVector<TimeType, SpaceVec> &b) { + return FourVector<TimeType, SpaceVec>(a.fTimeLike + b.fTimeLike, + a.fSpaceLike + b.fSpaceLike); +} + + /** + The math operator- + */ +template <typename TimeType, typename SpaceVec> +inline FourVector<TimeType, SpaceVec> +operator-(const FourVector<TimeType, SpaceVec> &a, + const FourVector<TimeType, SpaceVec> &b) { + return FourVector<TimeType, SpaceVec>(a.fTimeLike - b.fTimeLike, + a.fSpaceLike - b.fSpaceLike); +} + + /** + The math operator* + */ +template <typename TimeType, typename SpaceVec> +inline FourVector<TimeType, SpaceVec> +operator*(const FourVector<TimeType, SpaceVec> &a, const double b) { + return FourVector<TimeType, SpaceVec>(a.fTimeLike * b, a.fSpaceLike * b); +} + +/** + The math operator/ + */ + template <typename TimeType, typename SpaceVec> +inline FourVector<TimeType, SpaceVec> +operator/(const FourVector<TimeType, SpaceVec> &a, const double b) { + return FourVector<TimeType, SpaceVec>(a.fTimeLike / b, a.fSpaceLike / b); +} + +} // namespace corsika::geometry + +#endif diff --git a/Framework/Geometry/testFourVector.cc b/Framework/Geometry/testFourVector.cc new file mode 100644 index 000000000..8bc17488d --- /dev/null +++ b/Framework/Geometry/testFourVector.cc @@ -0,0 +1,187 @@ + +/** + * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one + // cpp file +#include <catch2/catch.hpp> + +#include <corsika/geometry/CoordinateSystem.h> +#include <corsika/geometry/FourVector.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> +#include <corsika/units/PhysicalUnits.h> +#include <cmath> + +#include <boost/type_index.hpp> +using boost::typeindex::type_id_with_cvr; + +using namespace corsika::geometry; +using namespace corsika::units::si; + + +TEST_CASE("four vectors") { + + // this is just needed as a baseline + CoordinateSystem& rootCS = + RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + + /* + Test: P2 = E2 - p2 all in [GeV] + This is the typical HEP application + */ + SECTION("Energy momentum in hep-units") { + + HEPEnergyType E0 = 10_GeV; + Vector<hepmomentum_d> P0(rootCS, {10_GeV, 10_GeV, 10_GeV}); + + FourVector p0(E0, P0); + + REQUIRE(p0.GetNormSqr() == -200_GeV * 1_GeV); + REQUIRE(p0.GetNorm() == sqrt(200_GeV * 1_GeV)); + } + + /* + Check space/time-like + */ + SECTION("Space/time likeness") { + + HEPEnergyType E0 = 20_GeV; + Vector<hepmomentum_d> P0(rootCS, {10_GeV, 0_GeV, 0_GeV}); + Vector<hepmomentum_d> P1(rootCS, {10_GeV, 10_GeV, 20_GeV}); + Vector<hepmomentum_d> P2(rootCS, {0_GeV, 20_GeV, 0_GeV}); + + FourVector p0(E0, P0); + FourVector p1(E0, P1); + FourVector p2(E0, P2); + + CHECK(p0.IsSpacelike()); + CHECK(!p0.IsTimelike()); + CHECK(!p0.IsPhotonlike()); + + CHECK(!p1.IsSpacelike()); + CHECK(p1.IsTimelike()); + CHECK(!p1.IsPhotonlike()); + + CHECK(!p2.IsSpacelike()); + CHECK(!p2.IsTimelike()); + CHECK(p2.IsPhotonlike()); + } + + /* + Test: P2 = E2/c2 - p2 with E in [GeV/c] and P in [GeV] + This requires additional factors of c + */ + SECTION("Energy momentum in SI-units") { + + auto E1 = 100_GeV / corsika::units::constants::c; + Vector<hepmomentum_d> P1(rootCS, {10_GeV, 5_GeV, 15_GeV}); + + FourVector p1(E1, P1); + + const double check = 100 * 100 - 10 * 10 - 5 * 5 - 15 * 15; // for dummies... + + REQUIRE(p1.GetNormSqr() / 1_GeV / 1_GeV == Approx(check)); + REQUIRE(p1.GetNorm() / 1_GeV == Approx(sqrt(check))); + } + + /** + Test: P2 = T2/c2 - r2 with T in [s] and r in [m] + This requires additional factors of c + */ + SECTION("Spacetime in SI-units") { + + TimeType T2 = 10_m / corsika::units::constants::c; + Vector<length_d> P2(rootCS, {10_m, 5_m, 5_m}); + + const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies... + + FourVector p2(T2, P2); + + REQUIRE(p2.GetNormSqr() == check * 1_m * 1_m); + REQUIRE(p2.GetNorm() == sqrt(abs(check)) * 1_m); + } + + /** + Testing the math operators + */ + + SECTION("Operators and comutions") { + + HEPEnergyType E1 = 100_GeV; + Vector<hepmomentum_d> P1(rootCS, {0_GeV, 0_GeV, 0_GeV}); + + HEPEnergyType E2 = 0_GeV; + Vector<hepmomentum_d> P2(rootCS, {10_GeV, 0_GeV, 0_GeV}); + + FourVector p1(E1, P1); + const FourVector p2(E2, P2); + + REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.)); + + SECTION("product") { + FourVector p3 = p1 + p2; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.))); + p3 -= p2; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.)); + } + + SECTION("difference") { + FourVector p3 = p1 - p2; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.))); + p3 += p2; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.)); + } + + SECTION("scale") { + double s = 10; + FourVector p3 = p1*s; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. * s * s))); + p3 /= 10; + REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. ))); + REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.)); + REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.)); + } + } + + /* + SECTION("Use as wrapper") { + + TimeType T1 = 10_m / corsika::units::constants::c; + Vector<length_d> P1(rootCS, {10_m, 5_m, 5_m}); + + const TimeType T2 = 10_m / corsika::units::constants::c; + const Vector<length_d> P2(rootCS, {10_m, 5_m, 5_m}); + + FourVector p1(T1, P1); + FourVector p2(T2, P2); + FourVector p3(TimeType(10_m/corsika::units::constants::c), Vector<length_d>(rootCS, + {10_m,10_m,10_m})); + + std::cout << type_id_with_cvr<decltype(p1)>().pretty_name() << std::endl; + std::cout << type_id_with_cvr<decltype(p2)>().pretty_name() << std::endl; + std::cout << type_id_with_cvr<decltype(p3)>().pretty_name() << std::endl; + + const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies... + REQUIRE(p1.GetNormSqr() == check * 1_m * 1_m); + REQUIRE(p2.GetNormSqr() == check * 1_m * 1_m); + REQUIRE(p3.GetNormSqr() == check * 1_m * 1_m); + + REQUIRE(p1.GetNorm() == sqrt(abs(check)) * 1_m); + REQUIRE(p2.GetNorm() == sqrt(abs(check)) * 1_m); + REQUIRE(p3.GetNorm() == sqrt(abs(check)) * 1_m); + } + */ +} -- GitLab