File indexing completed on 2026-01-09 09:26:47
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0010 #include "ActsExamples/Framework/IAlgorithm.hpp"
0011 #include "ActsExamples/Framework/IReader.hpp"
0012 #include "ActsExamples/Framework/IWriter.hpp"
0013 #include "ActsExamples/Framework/ProcessCode.hpp"
0014 #include "ActsExamples/Framework/RandomNumbers.hpp"
0015 #include "ActsExamples/Framework/SequenceElement.hpp"
0016 #include "ActsExamples/Framework/Sequencer.hpp"
0017 #include "ActsExamples/Framework/WhiteBoard.hpp"
0018 #include "ActsPython/Utilities/Helpers.hpp"
0019 #include "ActsPython/Utilities/Macros.hpp"
0020
0021 #include <pybind11/pybind11.h>
0022 #include <pybind11/stl.h>
0023
0024 namespace py = pybind11;
0025 using namespace py::literals;
0026 using namespace Acts;
0027 using namespace ActsExamples;
0028 using namespace ActsPython;
0029 using namespace ActsPlugins;
0030
0031 namespace {
0032 #if defined(__clang__)
0033 #pragma clang diagnostic push
0034 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
0035 #endif
0036 class PySequenceElement : public SequenceElement {
0037 public:
0038 using SequenceElement::SequenceElement;
0039
0040 std::string name() const override {
0041 py::gil_scoped_acquire acquire{};
0042 PYBIND11_OVERRIDE_PURE(std::string, SequenceElement, name);
0043 }
0044
0045 ProcessCode internalExecute(const AlgorithmContext& ctx) override {
0046 py::gil_scoped_acquire acquire{};
0047 PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, sysExecute, ctx);
0048 }
0049
0050 ProcessCode initialize() override {
0051 py::gil_scoped_acquire acquire{};
0052 PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, initialize);
0053 }
0054
0055 ProcessCode finalize() override {
0056 py::gil_scoped_acquire acquire{};
0057 PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, finalize);
0058 }
0059 };
0060 #if defined(__clang__)
0061 #pragma clang diagnostic pop
0062 #endif
0063
0064 class PyIAlgorithm : public IAlgorithm {
0065 public:
0066 using IAlgorithm::IAlgorithm;
0067
0068 ProcessCode execute(const AlgorithmContext& ctx) const override {
0069 py::gil_scoped_acquire acquire{};
0070 try {
0071 PYBIND11_OVERRIDE_PURE(ProcessCode, IAlgorithm, execute, ctx);
0072 } catch (py::error_already_set& e) {
0073 throw;
0074 } catch (std::runtime_error& e) {
0075 throw py::type_error("Python algorithm did not conform to interface");
0076 }
0077 }
0078
0079 std::string_view typeName() const override { return "Algorithm"; }
0080 };
0081
0082 void trigger_divbyzero() {
0083 volatile float j = 0.0;
0084 volatile float r = 123 / j;
0085 (void)r;
0086 }
0087
0088 void trigger_overflow() {
0089 volatile float j = std::numeric_limits<float>::max();
0090 volatile float r = j * j;
0091 (void)r;
0092 }
0093
0094 void trigger_invalid() {
0095 volatile float j = -1;
0096 volatile float r = std::sqrt(j);
0097 (void)r;
0098 }
0099
0100 }
0101
0102 namespace ActsPython {
0103 void addFramework(py::module& mex) {
0104 py::class_<IWriter, std::shared_ptr<IWriter>>(mex, "IWriter");
0105
0106 py::class_<IReader, std::shared_ptr<IReader>>(mex, "IReader");
0107
0108 py::class_<IContextDecorator, std::shared_ptr<IContextDecorator>>(
0109 mex, "IContextDecorator")
0110 .def("decorate", &IContextDecorator::decorate)
0111 .def("name", &IContextDecorator::name);
0112
0113 py::enum_<ProcessCode>(mex, "ProcessCode")
0114 .value("SUCCESS", ProcessCode::SUCCESS)
0115 .value("ABORT", ProcessCode::ABORT)
0116 .value("END", ProcessCode::END);
0117
0118 py::class_<WhiteBoard>(mex, "WhiteBoard")
0119 .def(py::init([](Logging::Level level, const std::string& name) {
0120 return std::make_unique<WhiteBoard>(getDefaultLogger(name, level));
0121 }),
0122 py::arg("level"), py::arg("name") = "WhiteBoard")
0123 .def("exists", &WhiteBoard::exists)
0124 .def_property_readonly("keys", &WhiteBoard::getKeys);
0125
0126 py::class_<AlgorithmContext>(mex, "AlgorithmContext")
0127 .def(py::init<std::size_t, std::size_t, WhiteBoard&, std::size_t>(),
0128 "alg"_a, "event"_a, "store"_a, "thread"_a)
0129 .def_readonly("algorithmNumber", &AlgorithmContext::algorithmNumber)
0130 .def_readonly("eventNumber", &AlgorithmContext::eventNumber)
0131 .def_property_readonly("eventStore",
0132 [](const AlgorithmContext& self) -> WhiteBoard& {
0133 return self.eventStore;
0134 })
0135 .def_readonly("threadId", &AlgorithmContext::threadId)
0136 .def_readonly("magFieldContext", &AlgorithmContext::magFieldContext)
0137 .def_readonly("geoContext", &AlgorithmContext::geoContext)
0138 .def_readonly("calibContext", &AlgorithmContext::calibContext)
0139 .def_readwrite("fpeMonitor", &AlgorithmContext::fpeMonitor);
0140
0141 auto pySequenceElement =
0142 py::class_<SequenceElement, PySequenceElement,
0143 std::shared_ptr<SequenceElement>>(mex, "SequenceElement")
0144 .def("internalExecute", &SequenceElement::internalExecute)
0145 .def("name", &SequenceElement::name);
0146
0147 auto bareAlgorithm =
0148 py::class_<IAlgorithm, std::shared_ptr<IAlgorithm>, SequenceElement,
0149 PyIAlgorithm>(mex, "IAlgorithm")
0150 .def(py::init_alias<const std::string&, Logging::Level>(),
0151 py::arg("name"), py::arg("level"))
0152 .def("execute", &IAlgorithm::execute);
0153
0154 using Config = Sequencer::Config;
0155 auto sequencer =
0156 py::class_<Sequencer>(mex, "_Sequencer")
0157 .def(py::init([](Config cfg) {
0158 cfg.iterationCallback = []() {
0159 py::gil_scoped_acquire gil;
0160 if (PyErr_CheckSignals() != 0) {
0161 throw py::error_already_set{};
0162 }
0163 };
0164 return std::make_unique<Sequencer>(cfg);
0165 }))
0166 .def("run",
0167 [](Sequencer& self) {
0168 py::gil_scoped_release gil;
0169 int res = self.run();
0170 if (res != EXIT_SUCCESS) {
0171 throw std::runtime_error{"Sequencer terminated abnormally"};
0172 }
0173 })
0174 .def("addContextDecorator", &Sequencer::addContextDecorator)
0175 .def("addAlgorithm", &Sequencer::addAlgorithm, py::keep_alive<1, 2>())
0176 .def("addReader", &Sequencer::addReader)
0177 .def("addWriter", &Sequencer::addWriter)
0178 .def("addWhiteboardAlias", &Sequencer::addWhiteboardAlias)
0179 .def_property_readonly("config", &Sequencer::config)
0180 .def_property_readonly("fpeResult", &Sequencer::fpeResult)
0181 .def_property_readonly_static(
0182 "_sourceLocation",
0183 [](py::object ) { return std::string{__FILE__}; });
0184
0185 auto c = py::class_<Config>(sequencer, "Config").def(py::init<>());
0186
0187 ACTS_PYTHON_STRUCT(c, skip, events, logLevel, numThreads, outputDir,
0188 outputTimingFile, trackFpes, fpeMasks, failOnFirstFpe,
0189 fpeStackTraceLength);
0190
0191 auto fpem =
0192 py::class_<Sequencer::FpeMask>(sequencer, "_FpeMask")
0193 .def(py::init<>())
0194 .def(py::init<std::string, std::pair<unsigned int, unsigned int>,
0195 FpeType, std::size_t>())
0196 .def("__repr__", [](const Sequencer::FpeMask& self) {
0197 std::stringstream ss;
0198 ss << self;
0199 return ss.str();
0200 });
0201
0202 ACTS_PYTHON_STRUCT(fpem, file, lines, type, count);
0203
0204 struct FpeMonitorContext {
0205 std::optional<FpeMonitor> mon;
0206 };
0207
0208 auto fpe = py::class_<FpeMonitor>(mex, "FpeMonitor")
0209 .def_static("_trigger_divbyzero", &trigger_divbyzero)
0210 .def_static("_trigger_overflow", &trigger_overflow)
0211 .def_static("_trigger_invalid", &trigger_invalid)
0212 .def_static("context", []() { return FpeMonitorContext(); });
0213
0214 fpe.def_property_readonly("result", py::overload_cast<>(&FpeMonitor::result),
0215 py::return_value_policy::reference_internal)
0216 .def("rearm", &FpeMonitor::rearm);
0217
0218 py::class_<FpeMonitor::Result>(fpe, "Result")
0219 .def("merged", &FpeMonitor::Result::merged)
0220 .def("merge", &FpeMonitor::Result::merge)
0221 .def("count", &FpeMonitor::Result::count)
0222 .def("__str__", [](const FpeMonitor::Result& result) {
0223 std::stringstream os;
0224 result.summary(os);
0225 return os.str();
0226 });
0227
0228 py::class_<FpeMonitorContext>(mex, "_FpeMonitorContext")
0229 .def(py::init([]() { return std::make_unique<FpeMonitorContext>(); }))
0230 .def(
0231 "__enter__",
0232 [](FpeMonitorContext& fm) -> FpeMonitor& {
0233 fm.mon.emplace();
0234 return fm.mon.value();
0235 },
0236 py::return_value_policy::reference_internal)
0237 .def("__exit__", [](FpeMonitorContext& fm, py::object ,
0238 py::object ,
0239 py::object ) { fm.mon.reset(); });
0240
0241 py::enum_<FpeType>(mex, "FpeType")
0242 .value("INTDIV", FpeType::INTDIV)
0243 .value("INTOVF", FpeType::INTOVF)
0244 .value("FLTDIV", FpeType::FLTDIV)
0245 .value("FLTOVF", FpeType::FLTOVF)
0246 .value("FLTUND", FpeType::FLTUND)
0247 .value("FLTRES", FpeType::FLTRES)
0248 .value("FLTINV", FpeType::FLTINV)
0249 .value("FLTSUB", FpeType::FLTSUB)
0250
0251 .def_property_readonly_static(
0252 "values", [](py::object ) -> const auto& {
0253 static const std::vector<FpeType> values = {
0254 FpeType::INTDIV, FpeType::INTOVF, FpeType::FLTDIV,
0255 FpeType::FLTOVF, FpeType::FLTUND, FpeType::FLTRES,
0256 FpeType::FLTINV, FpeType::FLTSUB};
0257 return values;
0258 });
0259
0260 py::register_exception<FpeFailure>(mex, "FpeFailure", PyExc_RuntimeError);
0261
0262 auto randomNumbers =
0263 py::class_<RandomNumbers, std::shared_ptr<RandomNumbers>>(mex,
0264 "RandomNumbers")
0265 .def(py::init<const RandomNumbers::Config&>());
0266
0267 {
0268 py::class_<RandomEngine>(mex, "RandomEngine").def(py::init<>());
0269
0270 py::class_<RandomNumbers::Config>(randomNumbers, "Config")
0271 .def(py::init<>())
0272 .def_readwrite("seed", &RandomNumbers::Config::seed);
0273 }
0274 }
0275
0276 }