Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-30 07:55:06

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Definitions/Algebra.hpp"
0010 #include "Acts/Definitions/Units.hpp"
0011 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0012 #include "Acts/Utilities/BoundingBox.hpp"
0013 #include "Acts/Utilities/Frustum.hpp"
0014 #include "Acts/Utilities/Ray.hpp"
0015 #include "ActsTests/CommonHelpers/BenchmarkTools.hpp"
0016 
0017 #include <algorithm>
0018 #include <chrono>
0019 #include <functional>
0020 #include <iostream>
0021 #include <map>
0022 #include <numbers>
0023 #include <random>
0024 #include <vector>
0025 
0026 using namespace Acts;
0027 using namespace ActsTests;
0028 
0029 struct O {};
0030 using Box = AxisAlignedBoundingBox<O, float, 3>;
0031 using VertexType = Box::VertexType;
0032 using vertex_array_type = Box::vertex_array_type;
0033 using value_type = Box::value_type;
0034 using Frustum3 = Frustum<float, 3, 4>;
0035 using Ray3 = Ray<float, 3>;
0036 
0037 int main(int /*argc*/, char** /*argv[]*/) {
0038   std::size_t n = 1000;
0039 
0040   std::mt19937 rng(42);
0041   std::uniform_real_distribution<float> dir(0, 1);
0042   std::uniform_real_distribution<float> loc(-10, 10);
0043   std::uniform_real_distribution<float> ang(std::numbers::pi / 10.,
0044                                             std::numbers::pi / 4.);
0045 
0046   Box testBox{nullptr, {0, 0, 0}, Box::Size{{1, 2, 3}}};
0047 
0048   std::cout << "\n==== RAY ====\n" << std::endl;
0049 
0050   std::map<std::string, bool (*)(const Box&, const Ray3&)> rayVariants;
0051 
0052   rayVariants["Nominal"] = [](const auto& box, const auto& ray) -> bool {
0053     return box.intersect(ray);
0054   };
0055 
0056   rayVariants["Incl. div., unroll"] = [](const Box& box,
0057                                          const Ray<float, 3>& ray) {
0058     const VertexType& origin = ray.origin();
0059 
0060     const vertex_array_type& d = ray.dir();
0061 
0062     double tmin = -INFINITY, tmax = INFINITY;
0063     if (d.x() != 0.0) {
0064       double tx1 = (box.min().x() - origin.x()) / d.x();
0065       double tx2 = (box.max().x() - origin.x()) / d.x();
0066 
0067       tmin = std::max(tmin, std::min(tx1, tx2));
0068       tmax = std::min(tmax, std::max(tx1, tx2));
0069     }
0070 
0071     if (d.y() != 0.0) {
0072       double ty1 = (box.min().y() - origin.y()) / d.y();
0073       double ty2 = (box.max().y() - origin.y()) / d.y();
0074 
0075       tmin = std::max(tmin, std::min(ty1, ty2));
0076       tmax = std::min(tmax, std::max(ty1, ty2));
0077     }
0078 
0079     if (d.z() != 0.0) {
0080       double tz1 = (box.min().z() - origin.z()) / d.z();
0081       double tz2 = (box.max().z() - origin.z()) / d.z();
0082 
0083       tmin = std::max(tmin, std::min(tz1, tz2));
0084       tmax = std::min(tmax, std::max(tz1, tz2));
0085     }
0086 
0087     return tmax > tmin && tmax > 0.0;
0088   };
0089 
0090   rayVariants["Incl. div., loop"] = [](const Box& box,
0091                                        const Ray<float, 3>& ray) {
0092     const VertexType& origin = ray.origin();
0093 
0094     const vertex_array_type& d = ray.dir();
0095 
0096     double tmin = -INFINITY, tmax = INFINITY;
0097 
0098     for (std::size_t i = 0; i < 3; i++) {
0099       if (d[i] == 0.0) {
0100         continue;
0101       }
0102       double t1 = (box.min()[i] - origin[i]) / d[i];
0103       double t2 = (box.max()[i] - origin[i]) / d[i];
0104       tmin = std::max(tmin, std::min(t1, t2));
0105       tmax = std::min(tmax, std::max(t1, t2));
0106     }
0107 
0108     return tmax > tmin && tmax > 0.0;
0109   };
0110 
0111   rayVariants["Incl. div., min/max alt., unroll"] =
0112       [](const Box& box, const Ray<float, 3>& ray) {
0113         const VertexType& origin = ray.origin();
0114         const vertex_array_type& d = ray.dir();
0115 
0116         double tx1 = (box.min().x() - origin.x()) / d.x();
0117         double tx2 = (box.max().x() - origin.x()) / d.x();
0118         double tmin = std::min(tx1, tx2);
0119         double tmax = std::max(tx1, tx2);
0120 
0121         double ty1 = (box.min().y() - origin.y()) / d.y();
0122         double ty2 = (box.max().y() - origin.y()) / d.y();
0123         tmin = std::max(tmin, std::min(ty1, ty2));
0124         tmax = std::min(tmax, std::max(ty1, ty2));
0125 
0126         double tz1 = (box.min().z() - origin.z()) / d.z();
0127         double tz2 = (box.max().z() - origin.z()) / d.z();
0128         tmin = std::max(tmin, std::min(tz1, tz2));
0129         tmax = std::min(tmax, std::max(tz1, tz2));
0130 
0131         return tmax > tmin && tmax > 0.0;
0132       };
0133 
0134   rayVariants["No div., min/max alt, unroll"] = [](const Box& box,
0135                                                    const Ray<float, 3>& ray) {
0136     const VertexType& origin = ray.origin();
0137     const vertex_array_type& id = ray.idir();
0138 
0139     double tx1 = (box.min().x() - origin.x()) * id.x();
0140     double tx2 = (box.max().x() - origin.x()) * id.x();
0141     double tmin = std::min(tx1, tx2);
0142     double tmax = std::max(tx1, tx2);
0143 
0144     double ty1 = (box.min().y() - origin.y()) * id.y();
0145     double ty2 = (box.max().y() - origin.y()) * id.y();
0146     tmin = std::max(tmin, std::min(ty1, ty2));
0147     tmax = std::min(tmax, std::max(ty1, ty2));
0148 
0149     double tz1 = (box.min().z() - origin.z()) * id.z();
0150     double tz2 = (box.max().z() - origin.z()) * id.z();
0151     tmin = std::max(tmin, std::min(tz1, tz2));
0152     tmax = std::min(tmax, std::max(tz1, tz2));
0153 
0154     return tmax > tmin && tmax > 0.0;
0155   };
0156 
0157   rayVariants["No div., min/max orig, loop"] = [](const Box& box,
0158                                                   const Ray<float, 3>& ray) {
0159     const VertexType& origin = ray.origin();
0160     const vertex_array_type& id = ray.idir();
0161     double tmin = -INFINITY, tmax = INFINITY;
0162 
0163     for (std::size_t i = 0; i < 3; i++) {
0164       double t1 = (box.min()[i] - origin[i]) * id[i];
0165       double t2 = (box.max()[i] - origin[i]) * id[i];
0166       tmin = std::max(tmin, std::min(t1, t2));
0167       tmax = std::min(tmax, std::max(t1, t2));
0168     }
0169 
0170     return tmax > tmin && tmax > 0.0;
0171   };
0172 
0173   using Vector3F = Eigen::Matrix<float, 3, 1>;
0174 
0175   std::vector<Ray3> rays{n, Ray3{Vector3F{0, 0, 0}, Vector3F{1, 0, 0}}};
0176   std::generate(rays.begin(), rays.end(), [&]() {
0177     const Vector3F d{dir(rng), dir(rng), dir(rng)};
0178     const Vector3F l{loc(rng), loc(rng), loc(rng)};
0179     return Ray3{l, d.normalized()};
0180   });
0181 
0182   std::cout << "Make sure ray implementations are identical" << std::endl;
0183   for (const auto& ray : rays) {
0184     std::vector<std::pair<std::string, bool>> results;
0185 
0186     std::transform(rayVariants.begin(), rayVariants.end(),
0187                    std::back_inserter(results),
0188                    [&](const auto& p) -> decltype(results)::value_type {
0189                      const auto& [name, func] = p;
0190                      return {name, func(testBox, ray)};
0191                    });
0192 
0193     bool all =
0194         std::ranges::all_of(results, [](const auto& r) { return r.second; });
0195     bool none =
0196         std::ranges::none_of(results, [](const auto& r) { return r.second; });
0197 
0198     if (!all && !none) {
0199       std::cerr << "Discrepancy: " << std::endl;
0200       for (const auto& [name, result] : results) {
0201         std::cerr << " - " << name << ": " << result << std::endl;
0202       }
0203 
0204       testBox.toStream(std::cerr);
0205       std::cerr << std::endl;
0206       std::cerr << "Ray: [" << ray.origin().transpose() << "], ["
0207                 << ray.dir().transpose() << "]" << std::endl;
0208       return -1;
0209     }
0210   }
0211   std::cout << "Seems ok" << std::endl;
0212 
0213   std::cout << "Run benchmarks: " << std::endl;
0214   for (const auto& p : rayVariants) {
0215     // can't capture structured binding, so pair access it is.
0216     std::cout << "- Benchmarking variant: '" << p.first << "'" << std::endl;
0217     auto bench_result = ActsTests::microBenchmark(
0218         [&](const auto& ray) { return p.second(testBox, ray); }, rays);
0219     std::cout << "  " << bench_result << std::endl;
0220   }
0221 
0222   std::cout << "\n==== FRUSTUM ====\n" << std::endl;
0223 
0224   std::map<std::string, bool (*)(const Box&, const Frustum3&)> frustumVariants;
0225 
0226   frustumVariants["Nominal"] = [](const auto& box,
0227                                   const auto& frustum) -> bool {
0228     return box.intersect(frustum);
0229   };
0230 
0231   frustumVariants["Manual constexpr loop unroll, early ret."] =
0232       [](const Box& box, const Frustum3& fr) {
0233         constexpr std::size_t sides = 4;  // yes this is pointless, I just want
0234                                           // to kind of match the other impl
0235 
0236         const auto& normals = fr.normals();
0237         const vertex_array_type fr_vmin = box.min() - fr.origin();
0238         const vertex_array_type fr_vmax = box.max() - fr.origin();
0239 
0240         auto calc = [&](const auto& normal) {
0241           return (normal.array() < 0).template cast<value_type>() * fr_vmin +
0242                  (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0243         };
0244 
0245         VertexType p_vtx;
0246 
0247         p_vtx = calc(normals[0]);
0248         if (p_vtx.dot(normals[0]) < 0) {
0249           return false;
0250         }
0251 
0252         p_vtx = calc(normals[1]);
0253         if (p_vtx.dot(normals[1]) < 0) {
0254           return false;
0255         }
0256 
0257         p_vtx = calc(normals[2]);
0258         if (p_vtx.dot(normals[2]) < 0) {
0259           return false;
0260         }
0261 
0262         if constexpr (sides > 2) {
0263           p_vtx = calc(normals[3]);
0264           if (p_vtx.dot(normals[3]) < 0) {
0265             return false;
0266           }
0267         }
0268 
0269         if constexpr (sides > 3) {
0270           p_vtx = calc(normals[4]);
0271           if (p_vtx.dot(normals[4]) < 0) {
0272             return false;
0273           }
0274         }
0275 
0276         if constexpr (sides > 4) {
0277           for (std::size_t i = 5; i <= fr.sides; i++) {
0278             const VertexType& normal = normals[i];
0279 
0280             p_vtx = calc(normal);
0281             if (p_vtx.dot(normal) < 0) {
0282               return false;
0283             }
0284           }
0285         }
0286 
0287         return true;
0288       };
0289 
0290   frustumVariants["Nominal, no early ret."] = [](const Box& box,
0291                                                  const Frustum3& fr) {
0292     const auto& normals = fr.normals();
0293     const vertex_array_type fr_vmin = box.min() - fr.origin();
0294     const vertex_array_type fr_vmax = box.max() - fr.origin();
0295 
0296     VertexType p_vtx;
0297     bool result = true;
0298     for (std::size_t i = 0; i < fr.sides + 1; i++) {
0299       const VertexType& normal = normals[i];
0300 
0301       p_vtx = (normal.array() < 0).template cast<value_type>() * fr_vmin +
0302               (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0303 
0304       result = result && (p_vtx.dot(normal) >= 0);
0305     }
0306     return result;
0307   };
0308 
0309   frustumVariants["Manual constexpr unroll, early ret."] =
0310       [](const Box& box, const Frustum3& fr) {
0311         constexpr std::size_t sides = 4;  // yes this is pointless, I just want
0312                                           // to kind of match the other impl
0313 
0314         const auto& normals = fr.normals();
0315         const vertex_array_type fr_vmin = box.min() - fr.origin();
0316         const vertex_array_type fr_vmax = box.max() - fr.origin();
0317 
0318         auto calc = [&](const auto& normal) {
0319           return (normal.array() < 0).template cast<value_type>() * fr_vmin +
0320                  (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0321         };
0322 
0323         VertexType p_vtx;
0324         bool result = true;
0325 
0326         p_vtx = calc(normals[0]);
0327         result = result && (p_vtx.dot(normals[0]) >= 0);
0328 
0329         p_vtx = calc(normals[1]);
0330         result = result && (p_vtx.dot(normals[1]) >= 0);
0331 
0332         p_vtx = calc(normals[2]);
0333         result = result && (p_vtx.dot(normals[2]) >= 0);
0334 
0335         if constexpr (sides > 2) {
0336           p_vtx = calc(normals[3]);
0337           result = result && (p_vtx.dot(normals[3]) >= 0);
0338         }
0339 
0340         if constexpr (sides > 3) {
0341           p_vtx = calc(normals[4]);
0342           result = result && (p_vtx.dot(normals[4]) >= 0);
0343         }
0344 
0345         if constexpr (sides > 4) {
0346           for (std::size_t i = 5; i <= fr.sides; i++) {
0347             const VertexType& normal = normals[i];
0348 
0349             p_vtx = calc(normal);
0350             result = result && (p_vtx.dot(normal) >= 0);
0351           }
0352         }
0353 
0354         return result;
0355       };
0356 
0357   std::vector<Frustum3> frustums{
0358       n, Frustum3{{0, 0, 0}, {1, 0, 0}, std::numbers::pi / 2.}};
0359   std::generate(frustums.begin(), frustums.end(), [&]() {
0360     const Vector3F d{dir(rng), dir(rng), dir(rng)};
0361     const Vector3F l{loc(rng), loc(rng), loc(rng)};
0362     return Frustum3{l, d.normalized(), ang(rng)};
0363   });
0364 
0365   std::cout << "Make sure frustum implementations are identical" << std::endl;
0366   for (const auto& fr : frustums) {
0367     std::vector<std::pair<std::string, bool>> results;
0368 
0369     std::transform(frustumVariants.begin(), frustumVariants.end(),
0370                    std::back_inserter(results),
0371                    [&](const auto& p) -> decltype(results)::value_type {
0372                      const auto& [name, func] = p;
0373                      return {name, func(testBox, fr)};
0374                    });
0375 
0376     bool all =
0377         std::ranges::all_of(results, [](const auto& r) { return r.second; });
0378     bool none =
0379         std::ranges::none_of(results, [](const auto& r) { return r.second; });
0380 
0381     if (!all && !none) {
0382       std::cerr << "Discrepancy: " << std::endl;
0383       for (const auto& [name, result] : results) {
0384         std::cerr << " - " << name << ": " << result << std::endl;
0385       }
0386 
0387       testBox.toStream(std::cerr);
0388       std::cerr << std::endl;
0389       std::cerr << "Frustum: [" << fr.origin().transpose() << "], ["
0390                 << fr.dir().transpose() << "]" << std::endl;
0391       return -1;
0392     }
0393   }
0394   std::cout << "Seems ok" << std::endl;
0395 
0396   std::size_t iters_per_run = 1000;
0397 
0398   std::vector<std::pair<std::string, Frustum3>> testFrusts = {
0399       {"away", Frustum3{{0, 0, -10}, {0, 0, -1}, std::numbers::pi / 4.}},
0400       {"towards", Frustum3{{0, 0, -10}, {0, 0, 1}, std::numbers::pi / 4.}},
0401       {"left", Frustum3{{0, 0, -10}, {0, 1, 0}, std::numbers::pi / 4.}},
0402       {"right", Frustum3{{0, 0, -10}, {0, -1, 0}, std::numbers::pi / 4.}},
0403       {"up", Frustum3{{0, 0, -10}, {1, 0, 0}, std::numbers::pi / 4.}},
0404       {"down", Frustum3{{0, 0, -10}, {-1, 0, 0}, std::numbers::pi / 4.}},
0405   };
0406 
0407   std::cout << "Run benchmarks: " << std::endl;
0408 
0409   for (const auto& fr_pair : testFrusts) {
0410     std::cout << "Frustum '" << fr_pair.first << "'" << std::endl;
0411 
0412     for (const auto& p : frustumVariants) {
0413       // can't capture structured binding, so pair access it is.
0414       std::cout << "- Benchmarking variant: '" << p.first << "'" << std::endl;
0415       auto bench_result = ActsTests::microBenchmark(
0416           [&]() { return p.second(testBox, fr_pair.second); }, iters_per_run);
0417       std::cout << "  " << bench_result << std::endl;
0418     }
0419 
0420     std::cout << std::endl;
0421   }
0422   return 0;
0423 }