Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:10:22

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