File indexing completed on 2026-05-27 07:24:23
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "detray/test/common/track_generators.hpp"
0011
0012 #include "detray/definitions/units.hpp"
0013 #include "detray/tracks/tracks.hpp"
0014 #include "detray/tracks/trajectories.hpp"
0015
0016
0017 #include "detray/test/framework/types.hpp"
0018 #include "detray/test/utils/statistics.hpp"
0019
0020
0021 #include <gtest/gtest.h>
0022
0023 using namespace detray;
0024
0025 using test_algebra = test::algebra;
0026 using scalar = test::scalar;
0027 using vector3 = test::vector3;
0028 using point3 = test::point3;
0029
0030 GTEST_TEST(detray_simulation, uniform_track_generator) {
0031 using generator_t =
0032 uniform_track_generator<free_track_parameters<test_algebra>>;
0033
0034 constexpr const scalar tol{1e-5f};
0035 constexpr const scalar max_pi{generator_t::configuration::k_max_pi};
0036
0037 constexpr std::size_t phi_steps{50u};
0038 constexpr std::size_t theta_steps{50u};
0039 constexpr const scalar charge{-1.f};
0040
0041 std::array<vector3, phi_steps * theta_steps> momenta{};
0042
0043
0044 for (std::size_t itheta{0u}; itheta < theta_steps; ++itheta) {
0045 const scalar theta{static_cast<scalar>(itheta) * max_pi /
0046 static_cast<scalar>(theta_steps - 1u)};
0047
0048
0049 for (std::size_t iphi{0u}; iphi < phi_steps; ++iphi) {
0050
0051 const scalar phi{-constant<scalar>::pi +
0052 static_cast<scalar>(iphi) *
0053 (constant<scalar>::pi + max_pi) /
0054 static_cast<scalar>(phi_steps)};
0055
0056
0057 vector3 mom{std::cos(phi) * std::sin(theta),
0058 std::sin(phi) * std::sin(theta), std::cos(theta)};
0059 mom = vector::normalize(mom);
0060 free_track_parameters<test_algebra> traj({0.f, 0.f, 0.f}, 0.f, mom, -1.f);
0061
0062 momenta[itheta * phi_steps + iphi] = traj.mom(-1.f);
0063 }
0064 }
0065
0066
0067 std::size_t n_tracks{0u};
0068 generator_t::configuration trk_gen_cfg{};
0069 trk_gen_cfg.charge(charge);
0070 trk_gen_cfg.phi_steps(phi_steps).theta_steps(theta_steps);
0071
0072 for (const auto track : generator_t(trk_gen_cfg)) {
0073 vector3& expected = momenta[n_tracks];
0074 vector3 result = track.mom(trk_gen_cfg.charge());
0075
0076
0077 EXPECT_NEAR(vector::norm(expected - result), 0.f, tol)
0078 << "Track: \n"
0079 << expected[0] << "\t" << result[0] << "\n"
0080 << expected[1] << "\t" << result[1] << "\n"
0081 << expected[2] << "\t" << result[2] << std::endl;
0082
0083 ++n_tracks;
0084 }
0085 ASSERT_EQ(momenta.size(), n_tracks);
0086
0087
0088 n_tracks = 0u;
0089 for (const auto r : generator_t(phi_steps, theta_steps)) {
0090 vector3& expected = momenta[n_tracks];
0091 vector3 result = r.dir();
0092
0093
0094 EXPECT_NEAR(vector::norm(expected - result), 0.f, tol)
0095 << "Ray: \n"
0096 << expected[0] << "\t" << result[0] << "\n"
0097 << expected[1] << "\t" << result[1] << "\n"
0098 << expected[2] << "\t" << result[2] << std::endl;
0099
0100 ++n_tracks;
0101 }
0102 ASSERT_EQ(momenta.size(), n_tracks);
0103
0104
0105 const vector3 B{0.f * unit<scalar>::T, 0.f * unit<scalar>::T,
0106 2.f * unit<scalar>::T};
0107 n_tracks = 0u;
0108 for (const auto track : generator_t(phi_steps, theta_steps)) {
0109 detail::helix<test_algebra> helix_traj(track, B);
0110 vector3& expected = momenta[n_tracks];
0111 vector3 result = helix_traj.dir(0.f);
0112
0113
0114 EXPECT_NEAR(vector::norm(expected - result), 0.f, tol)
0115 << "Helix: \n"
0116 << expected[0] << "\t" << result[0] << "\n"
0117 << expected[1] << "\t" << result[1] << "\n"
0118 << expected[2] << "\t" << result[2] << std::endl;
0119
0120 ++n_tracks;
0121 }
0122 ASSERT_EQ(momenta.size(), n_tracks);
0123 }
0124
0125 GTEST_TEST(detray_simulation, uniform_track_generator_eta) {
0126 using generator_t =
0127 uniform_track_generator<free_track_parameters<test_algebra>>;
0128
0129 constexpr const scalar tol{1e-5f};
0130 constexpr const scalar max_pi{generator_t::configuration::k_max_pi};
0131 constexpr const scalar charge{-1.f};
0132
0133 constexpr std::size_t phi_steps{50u};
0134 constexpr std::size_t eta_steps{50u};
0135
0136 std::array<vector3, phi_steps * eta_steps> momenta{};
0137
0138
0139 for (std::size_t ieta{0u}; ieta < eta_steps; ++ieta) {
0140 const scalar eta{-5.f + static_cast<scalar>(ieta) * 10.f /
0141 static_cast<scalar>(eta_steps - 1u)};
0142 const scalar theta{2.f * std::atan(std::exp(-eta))};
0143
0144
0145 for (std::size_t iphi{0u}; iphi < phi_steps; ++iphi) {
0146
0147 const scalar phi{-constant<scalar>::pi +
0148 static_cast<scalar>(iphi) *
0149 (constant<scalar>::pi + max_pi) /
0150 static_cast<scalar>(phi_steps)};
0151
0152
0153 vector3 mom{std::cos(phi) * std::sin(theta),
0154 std::sin(phi) * std::sin(theta), std::cos(theta)};
0155 mom = vector::normalize(mom);
0156 free_track_parameters<test_algebra> traj({0.f, 0.f, 0.f}, 0.f, mom, -1.f);
0157
0158 momenta[ieta * phi_steps + iphi] = traj.mom(charge);
0159 }
0160 }
0161
0162
0163 std::size_t n_tracks{0u};
0164
0165 auto trk_generator = generator_t{};
0166 trk_generator.config().phi_steps(phi_steps).eta_steps(eta_steps);
0167 trk_generator.config().charge(charge);
0168
0169 for (const auto track : trk_generator) {
0170 vector3& expected = momenta[n_tracks];
0171 vector3 result = track.mom(trk_generator.config().charge());
0172
0173
0174 EXPECT_NEAR(vector::norm(expected - result), 0.f, tol)
0175 << "Expected\tResult: \n"
0176 << expected[0] << "\t" << result[0] << "\n"
0177 << expected[1] << "\t" << result[1] << "\n"
0178 << expected[2] << "\t" << result[2] << std::endl;
0179
0180 ++n_tracks;
0181 }
0182 ASSERT_EQ(momenta.size(), n_tracks);
0183 }
0184
0185 GTEST_TEST(detray_simulation, uniform_track_generator_with_range) {
0186 using generator_t =
0187 uniform_track_generator<free_track_parameters<test_algebra>>;
0188
0189 constexpr const scalar tol{1e-5f};
0190
0191 std::vector<std::array<scalar, 2>> theta_phi;
0192
0193 auto trk_gen_cfg = generator_t::configuration{};
0194 trk_gen_cfg.phi_range(-2.f, 2.f).phi_steps(4u);
0195 trk_gen_cfg.theta_range(1.f, 2.f).theta_steps(2u);
0196
0197 for (const auto track : generator_t{trk_gen_cfg}) {
0198 const auto dir = track.dir();
0199 theta_phi.push_back({vector::theta(dir), vector::phi(dir)});
0200 }
0201
0202 EXPECT_EQ(theta_phi.size(), 8u);
0203 EXPECT_NEAR(theta_phi[0][0], 1.f, tol);
0204 EXPECT_NEAR(theta_phi[0][1], -2.f, tol);
0205 EXPECT_NEAR(theta_phi[1][0], 1.f, tol);
0206 EXPECT_NEAR(theta_phi[1][1], -1.f, tol);
0207 EXPECT_NEAR(theta_phi[2][0], 1.f, tol);
0208 EXPECT_NEAR(theta_phi[2][1], 0.f, tol);
0209 EXPECT_NEAR(theta_phi[3][0], 1.f, tol);
0210 EXPECT_NEAR(theta_phi[3][1], 1.f, tol);
0211 EXPECT_NEAR(theta_phi[4][0], 2.f, tol);
0212 EXPECT_NEAR(theta_phi[4][1], -2.f, tol);
0213 EXPECT_NEAR(theta_phi[5][0], 2.f, tol);
0214 EXPECT_NEAR(theta_phi[5][1], -1.f, tol);
0215 EXPECT_NEAR(theta_phi[6][0], 2.f, tol);
0216 EXPECT_NEAR(theta_phi[6][1], 0.f, tol);
0217 EXPECT_NEAR(theta_phi[7][0], 2.f, tol);
0218 EXPECT_NEAR(theta_phi[7][1], 1.f, tol);
0219 }
0220
0221
0222 GTEST_TEST(detray_simulation, random_track_generator_uniform) {
0223
0224 using uniform_gen_t =
0225 detail::random_numbers<scalar, std::uniform_real_distribution<scalar>>;
0226 using trk_generator_t =
0227 random_track_generator<free_track_parameters<test_algebra>,
0228 uniform_gen_t>;
0229
0230
0231 constexpr scalar tol{0.02f};
0232
0233
0234 std::size_t n_tracks{0u};
0235 constexpr std::size_t n_gen_tracks{10000u};
0236
0237
0238 trk_generator_t::configuration trk_gen_cfg{};
0239 trk_gen_cfg.n_tracks(n_gen_tracks);
0240 trk_gen_cfg.seed(42u);
0241 trk_gen_cfg.phi_range(-0.9f * constant<scalar>::pi,
0242 0.8f * constant<scalar>::pi);
0243 trk_gen_cfg.eta_range(-4.f, 4.f);
0244 trk_gen_cfg.mom_range(1.f * unit<scalar>::GeV, 2.f * unit<scalar>::GeV);
0245 trk_gen_cfg.origin_stddev(0.1f * unit<scalar>::mm, 0.f * unit<scalar>::mm,
0246 0.2f * unit<scalar>::mm);
0247
0248
0249 std::array<scalar, n_gen_tracks> x{};
0250 std::array<scalar, n_gen_tracks> y{};
0251 std::array<scalar, n_gen_tracks> z{};
0252 std::array<scalar, n_gen_tracks> mom{};
0253 std::array<scalar, n_gen_tracks> phi{};
0254 std::array<scalar, n_gen_tracks> theta{};
0255
0256 for (const auto track : trk_generator_t{trk_gen_cfg}) {
0257 const auto pos = track.pos();
0258 x[n_tracks] = pos[0];
0259 y[n_tracks] = pos[1];
0260 z[n_tracks] = pos[2];
0261 mom[n_tracks] = track.p(trk_gen_cfg.charge());
0262 phi[n_tracks] = vector::phi(track.dir());
0263 theta[n_tracks] = vector::theta(track.dir());
0264
0265 ++n_tracks;
0266 }
0267
0268 ASSERT_EQ(n_gen_tracks, n_tracks);
0269
0270
0271 const auto& ori = trk_gen_cfg.origin();
0272 const auto& ori_stddev = trk_gen_cfg.origin_stddev();
0273 const auto& phi_range = trk_gen_cfg.phi_range();
0274 const auto& theta_range = trk_gen_cfg.theta_range();
0275 ASSERT_NEAR(theta_range[0], 0.0366f, tol);
0276 ASSERT_NEAR(theta_range[1], 3.105f, tol);
0277 const auto& mom_range = trk_gen_cfg.mom_range();
0278
0279
0280 EXPECT_NEAR(statistics::mean(x), ori[0], tol);
0281 EXPECT_NEAR(statistics::mean(y), ori[1], tol);
0282 EXPECT_NEAR(statistics::mean(z), ori[2], tol);
0283 EXPECT_NEAR(statistics::mean(mom), 0.5f * (mom_range[0] + mom_range[1]), tol);
0284 EXPECT_NEAR(statistics::mean(phi), 0.5f * (phi_range[0] + phi_range[1]), tol);
0285 EXPECT_NEAR(statistics::mean(theta), 0.5f * (theta_range[0] + theta_range[1]),
0286 tol);
0287
0288
0289 EXPECT_NEAR(statistics::variance(x), ori_stddev[0] * ori_stddev[0], tol);
0290 EXPECT_NEAR(statistics::variance(y), ori_stddev[1] * ori_stddev[1], tol);
0291 EXPECT_NEAR(statistics::variance(z), ori_stddev[2] * ori_stddev[2], tol);
0292 EXPECT_NEAR(statistics::variance(mom),
0293 1.0f / 12.0f * std::pow(mom_range[1] - mom_range[0], 2.f), tol);
0294 EXPECT_NEAR(statistics::variance(phi),
0295 1.0f / 12.0f * std::pow(phi_range[1] - phi_range[0], 2.f), tol);
0296 EXPECT_NEAR(statistics::variance(theta),
0297 1.0f / 12.0f * std::pow(theta_range[1] - theta_range[0], 2.f),
0298 tol);
0299 }
0300
0301
0302 GTEST_TEST(detray_simulation, random_track_generator_normal) {
0303
0304 using normal_gen_t =
0305 detail::random_numbers<scalar, std::normal_distribution<scalar>>;
0306 using trk_generator_t =
0307 random_track_generator<free_track_parameters<test_algebra>, normal_gen_t>;
0308
0309
0310 constexpr scalar tol{0.02f};
0311
0312
0313 std::size_t n_tracks{0u};
0314 constexpr std::size_t n_gen_tracks{10000u};
0315
0316
0317 trk_generator_t::configuration trk_gen_cfg{};
0318 trk_gen_cfg.n_tracks(n_gen_tracks);
0319 trk_gen_cfg.seed(42u);
0320 trk_gen_cfg.phi_range(-0.9f * constant<scalar>::pi,
0321 0.8f * constant<scalar>::pi);
0322 trk_gen_cfg.eta_range(-4.f, 4.f);
0323 trk_gen_cfg.mom_range(1.f * unit<scalar>::GeV, 2.f * unit<scalar>::GeV);
0324 trk_gen_cfg.origin(0.f, 0.f, 0.f);
0325 trk_gen_cfg.origin_stddev(0.1f * unit<scalar>::mm, 0.5f * unit<scalar>::mm,
0326 0.3f * unit<scalar>::mm);
0327
0328
0329 std::array<scalar, n_gen_tracks> x{};
0330 std::array<scalar, n_gen_tracks> y{};
0331 std::array<scalar, n_gen_tracks> z{};
0332 std::array<scalar, n_gen_tracks> mom{};
0333 std::array<scalar, n_gen_tracks> phi{};
0334 std::array<scalar, n_gen_tracks> theta{};
0335
0336 for (const auto track : trk_generator_t{trk_gen_cfg}) {
0337 const auto pos = track.pos();
0338 x[n_tracks] = pos[0];
0339 y[n_tracks] = pos[1];
0340 z[n_tracks] = pos[2];
0341 mom[n_tracks] = track.p(trk_gen_cfg.charge());
0342 phi[n_tracks] = vector::phi(track.dir());
0343 theta[n_tracks] = vector::theta(track.dir());
0344
0345 ++n_tracks;
0346 }
0347
0348 ASSERT_EQ(n_gen_tracks, n_tracks);
0349
0350
0351 const auto& ori = trk_gen_cfg.origin();
0352 const auto& ori_stddev = trk_gen_cfg.origin_stddev();
0353 const auto& phi_range = trk_gen_cfg.phi_range();
0354 const auto& theta_range = trk_gen_cfg.theta_range();
0355 ASSERT_NEAR(theta_range[0], 0.0366f, tol);
0356 ASSERT_NEAR(theta_range[1], 3.105f, tol);
0357 const auto& mom_range = trk_gen_cfg.mom_range();
0358
0359
0360 EXPECT_NEAR(statistics::mean(x), ori[0], tol);
0361 EXPECT_NEAR(statistics::mean(y), ori[1], tol);
0362 EXPECT_NEAR(statistics::mean(z), ori[2], tol);
0363 EXPECT_NEAR(statistics::mean(mom),
0364 mom_range[0] + 0.5f * (mom_range[1] - mom_range[0]), tol);
0365 EXPECT_NEAR(statistics::mean(phi),
0366 phi_range[0] + 0.5f * (phi_range[1] - phi_range[0]), tol);
0367 EXPECT_NEAR(statistics::mean(theta),
0368 theta_range[0] + 0.5f * (theta_range[1] - theta_range[0]), tol);
0369
0370
0371 EXPECT_NEAR(statistics::variance(x), ori_stddev[0] * ori_stddev[0], tol);
0372 EXPECT_NEAR(statistics::variance(y), ori_stddev[1] * ori_stddev[1], tol);
0373 EXPECT_NEAR(statistics::variance(z), ori_stddev[2] * ori_stddev[2], tol);
0374 EXPECT_NEAR(statistics::variance(mom),
0375 std::pow(0.5f / 3.0f * (mom_range[1] - mom_range[0]), 2.f), tol);
0376 EXPECT_NEAR(statistics::variance(phi),
0377 std::pow(0.5f / 3.0f * (phi_range[1] - phi_range[0]), 2.f), tol);
0378 EXPECT_NEAR(statistics::variance(theta),
0379 std::pow(0.5f / 3.0f * (theta_range[1] - theta_range[0]), 2.f),
0380 tol);
0381 }