Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:27

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