File indexing completed on 2026-09-05 08:18:37
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "detray/core/detector.hpp"
0011
0012
0013 #include "detray/propagator/propagation_config.hpp"
0014
0015
0016 #include "detray/test/common/event_generator/uniform_track_generator_config.hpp"
0017
0018
0019 #include "detray/test/cpu/material_scan.hpp"
0020 #include "detray/test/cpu/material_validation.hpp"
0021 #include "detray/test/framework/register_checks.hpp"
0022 #include "detray/test/framework/test_configuration.hpp"
0023 #include "detray/test/framework/whiteboard.hpp"
0024 #include "detray/test/validation/material_validation_config.hpp"
0025
0026
0027 #include "algebra/array.hpp"
0028 #include "detray/definitions/algebra.hpp"
0029 #include "detray/detectors/default_metadata.hpp"
0030
0031
0032 #include <gtest/gtest.h>
0033
0034
0035 #include <pybind11/pybind11.h>
0036 #include <pybind11/stl.h>
0037
0038
0039 #include <cstddef>
0040 #include <cstdint>
0041 #include <functional>
0042 #include <memory>
0043 #include <sstream>
0044 #include <stdexcept>
0045 #include <string>
0046 #include <tuple>
0047 #include <utility>
0048
0049 namespace py = pybind11;
0050
0051 namespace {
0052
0053 using scalar_t = DETRAY_CUSTOM_SCALARTYPE;
0054 using algebra_t = detray::array<scalar_t>;
0055 using detector_t = detray::detector<detray::default_metadata<algebra_t>>;
0056 using material_scan_config_t = detray::test::material_scan<detector_t>::config;
0057 using propagation_config_t = detray::propagation::config;
0058 using track_generator_config_t =
0059 detray::uniform_track_generator_config<scalar_t>;
0060 using material_validation_config_t =
0061 detray::test::material_validation_config<algebra_t>;
0062 using base_config_t = detray::test::configuration<scalar_t>;
0063
0064 template <typename T>
0065 std::string to_string(const T &obj) {
0066 std::ostringstream os;
0067 os << obj;
0068 return os.str();
0069 }
0070
0071
0072
0073
0074 int run_gtest(const std::function<void()> ®ister_checks_fn) {
0075 static bool already_run = false;
0076 if (already_run) {
0077 throw std::runtime_error("gtest can only be run once per process");
0078 }
0079 already_run = true;
0080
0081 ::testing::InitGoogleTest();
0082 register_checks_fn();
0083 return RUN_ALL_TESTS();
0084 }
0085
0086
0087 int run_material_validation(const detector_t &det,
0088 const detray::name_map &names,
0089 const material_scan_config_t &scan_cfg,
0090 const material_validation_config_t &val_cfg) {
0091 detector_t::geometry_context ctx{};
0092 auto wb = std::make_shared<detray::test::whiteboard>();
0093
0094 return run_gtest([&] {
0095 detray::test::register_checks<detray::test::material_scan>(
0096 det, names, scan_cfg, ctx, wb);
0097 detray::test::register_checks<detray::test::material_validation>(
0098 det, names, val_cfg, ctx, wb);
0099 });
0100 }
0101
0102 }
0103
0104 PYBIND11_MODULE(DetrayTestsPythonBindings, m) {
0105 m.doc() = "Detray tests bindings";
0106
0107 py::class_<track_generator_config_t>(m, "TrackGeneratorConfig")
0108 .def(py::init<>())
0109 .def_property(
0110 "seed", [](const track_generator_config_t &c) { return c.seed(); },
0111 [](track_generator_config_t &c, std::uint64_t s) { c.seed(s); },
0112 "Monte-Carlo seed")
0113 .def(
0114 "nTracks",
0115 [](const track_generator_config_t &c) { return c.n_tracks(); },
0116 "Total number of tracks")
0117 .def_property(
0118 "phiRange",
0119 [](const track_generator_config_t &c) {
0120 const auto r = c.phi_range();
0121 return std::make_pair(r[0], r[1]);
0122 },
0123 [](track_generator_config_t &c,
0124 const std::pair<scalar_t, scalar_t> &r) {
0125 c.phi_range(r.first, r.second);
0126 },
0127 "Phi range (min, max) (native rad)")
0128 .def_property(
0129 "thetaRange",
0130 [](const track_generator_config_t &c) {
0131 const auto r = c.theta_range();
0132 return std::make_pair(r[0], r[1]);
0133 },
0134 [](track_generator_config_t &c,
0135 const std::pair<scalar_t, scalar_t> &r) {
0136 c.theta_range(r.first, r.second);
0137 },
0138 "Theta range (min, max) (native rad)")
0139 .def_property(
0140 "etaRange",
0141 [](const track_generator_config_t &c) {
0142 const auto r = c.eta_range();
0143 return std::make_pair(r[0], r[1]);
0144 },
0145 [](track_generator_config_t &c,
0146 const std::pair<scalar_t, scalar_t> &r) {
0147 c.eta_range(r.first, r.second);
0148 },
0149 "Eta range (min, max)")
0150 .def_property(
0151 "phiSteps",
0152 [](const track_generator_config_t &c) { return c.phi_steps(); },
0153 [](track_generator_config_t &c, std::size_t n) { c.phi_steps(n); },
0154 "Number of phi steps")
0155 .def_property(
0156 "thetaSteps",
0157 [](const track_generator_config_t &c) { return c.theta_steps(); },
0158 [](track_generator_config_t &c, std::size_t n) { c.theta_steps(n); },
0159 "Number of theta steps")
0160 .def_property(
0161 "etaSteps",
0162 [](const track_generator_config_t &c) { return c.eta_steps(); },
0163 [](track_generator_config_t &c, std::size_t n) { c.eta_steps(n); },
0164 "Number of eta steps")
0165 .def_property(
0166 "uniformEta",
0167 [](const track_generator_config_t &c) { return c.uniform_eta(); },
0168 [](track_generator_config_t &c, bool b) { c.uniform_eta(b); },
0169 "Whether to step uniformly in eta")
0170 .def_property(
0171 "origin",
0172 [](const track_generator_config_t &c) {
0173 const auto &o = c.origin();
0174 return std::make_tuple(o[0], o[1], o[2]);
0175 },
0176 [](track_generator_config_t &c,
0177 const std::tuple<scalar_t, scalar_t, scalar_t> &o) {
0178 c.origin(std::get<0>(o), std::get<1>(o), std::get<2>(o));
0179 },
0180 "Track origin")
0181 .def(
0182 "pT",
0183 [](track_generator_config_t &c,
0184 scalar_t p) -> track_generator_config_t & { return c.p_T(p); },
0185 py::arg("p"), py::return_value_policy::reference,
0186 "Set the transverse momentum magnitude")
0187 .def(
0188 "pTot",
0189 [](track_generator_config_t &c,
0190 scalar_t p) -> track_generator_config_t & { return c.p_tot(p); },
0191 py::arg("p"), py::return_value_policy::reference,
0192 "Set the total momentum magnitude")
0193 .def(
0194 "momRange",
0195 [](const track_generator_config_t &c) {
0196 const auto r = c.mom_range();
0197 return std::make_pair(r[0], r[1]);
0198 },
0199 "Momentum range")
0200 .def_property(
0201 "randomizeCharge",
0202 [](const track_generator_config_t &c) {
0203 return c.randomize_charge();
0204 },
0205 [](track_generator_config_t &c, bool b) { c.randomize_charge(b); },
0206 "Randomly flip the charge sign")
0207 .def_property(
0208 "time", [](const track_generator_config_t &c) { return c.time(); },
0209 [](track_generator_config_t &c, scalar_t t) { c.time(t); },
0210 "Track time")
0211 .def_property(
0212 "charge",
0213 [](const track_generator_config_t &c) { return c.charge(); },
0214 [](track_generator_config_t &c, scalar_t q) { c.charge(q); },
0215 "Track charge")
0216 .def(
0217 "isPT", [](const track_generator_config_t &c) { return c.is_pT(); },
0218 "Whether the momentum magnitude is interpreted as transverse")
0219 .def("__repr__", &to_string<track_generator_config_t>);
0220
0221 py::class_<base_config_t>(m, "TestConfig")
0222 .def_property(
0223 "tol", [](const base_config_t &c) { return c.tol(); },
0224 [](base_config_t &c, scalar_t t) { c.tol(t); },
0225 "Tolerance to compare two floating point values")
0226 .def_property(
0227 "propagation",
0228 [](base_config_t &c) -> propagation_config_t & {
0229 return c.propagation();
0230 },
0231 [](base_config_t &c, const propagation_config_t &v) {
0232 c.propagation() = v;
0233 },
0234 py::return_value_policy::reference_internal,
0235 "Propagation configuration");
0236
0237 py::class_<material_validation_config_t, base_config_t>(
0238 m, "MaterialValidationConfig")
0239 .def(py::init<>())
0240 .def_property(
0241 "name",
0242 [](const material_validation_config_t &c) { return c.name(); },
0243 [](material_validation_config_t &c, const std::string &n) {
0244 c.name(n);
0245 },
0246 "Name of the test")
0247 .def_property(
0248 "materialFile",
0249 [](const material_validation_config_t &c) {
0250 return c.material_file();
0251 },
0252 [](material_validation_config_t &c, const std::string &f) {
0253 c.material_file(f);
0254 },
0255 "Name of the output file with the navigation material traces")
0256 .def_property(
0257 "nTracks",
0258 [](const material_validation_config_t &c) { return c.n_tracks(); },
0259 [](material_validation_config_t &c, std::size_t n) { c.n_tracks(n); },
0260 "Maximal number of test tracks to run")
0261 .def_property(
0262 "relativeError",
0263 [](const material_validation_config_t &c) {
0264 return c.relative_error();
0265 },
0266 [](material_validation_config_t &c, scalar_t re) {
0267 c.relative_error(re);
0268 },
0269 "Allowed relative discrepancy between truth and navigation material")
0270 .def("__repr__", &to_string<material_validation_config_t>);
0271
0272 py::class_<material_scan_config_t, base_config_t>(m, "MaterialScanConfig")
0273 .def(py::init<>())
0274 .def_property(
0275 "name", [](const material_scan_config_t &c) { return c.name(); },
0276 [](material_scan_config_t &c, const std::string &n) { c.name(n); },
0277 "Name of the test")
0278 .def_property(
0279 "materialFile",
0280 [](const material_scan_config_t &c) { return c.material_file(); },
0281 [](material_scan_config_t &c, const std::string &f) {
0282 c.material_file(f);
0283 },
0284 "Name of the output file with the material traces")
0285 .def_property(
0286 "overlapsRemoval",
0287 [](const material_scan_config_t &c) { return c.overlaps_removal(); },
0288 [](material_scan_config_t &c, bool o) { c.overlaps_removal(o); },
0289 "Perform overlaps removal")
0290 .def_property(
0291 "overlapsTol",
0292 [](const material_scan_config_t &c) { return c.overlaps_tol(); },
0293 [](material_scan_config_t &c, scalar_t t) { c.overlaps_tol(t); },
0294 "Tolerance for considering surfaces to be overlapping")
0295 .def_property(
0296 "trackGenerator",
0297 [](material_scan_config_t &c) -> track_generator_config_t & {
0298 return c.track_generator();
0299 },
0300 [](material_scan_config_t &c, const track_generator_config_t &v) {
0301 c.track_generator() = v;
0302 },
0303 py::return_value_policy::reference_internal,
0304 "Track generator configuration")
0305 .def("__repr__", &to_string<material_scan_config_t>);
0306
0307 m.def("runMaterialValidation", &run_material_validation, py::arg("detector"),
0308 py::arg("names"), py::arg("scanConfig"), py::arg("validationConfig"),
0309 "Run the CPU material validation for the given detector.");
0310 }