Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Framework.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Framework.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
19 
20 #include <pybind11/pybind11.h>
21 #include <pybind11/stl.h>
22 
23 namespace py = pybind11;
24 using namespace ActsExamples;
25 using namespace Acts::Python;
26 
27 namespace {
28 #if defined(__clang__)
29 #pragma clang diagnostic push
30 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
31 #endif
32 class PySequenceElement : public SequenceElement {
33  public:
34  using SequenceElement::SequenceElement;
35 
36  std::string name() const override {
37  py::gil_scoped_acquire acquire{};
38  PYBIND11_OVERRIDE_PURE(std::string, SequenceElement, name);
39  }
40 
41  ProcessCode internalExecute(const AlgorithmContext& ctx) override {
42  py::gil_scoped_acquire acquire{};
43  PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, sysExecute, ctx);
44  }
45 
46  ProcessCode initialize() override {
47  py::gil_scoped_acquire acquire{};
48  PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, initialize);
49  }
50 
51  ProcessCode finalize() override {
52  py::gil_scoped_acquire acquire{};
53  PYBIND11_OVERRIDE_PURE(ProcessCode, SequenceElement, finalize);
54  }
55 };
56 #if defined(__clang__)
57 #pragma clang diagnostic pop
58 #endif
59 
60 class PyIAlgorithm : public IAlgorithm {
61  public:
63 
64  ProcessCode execute(const AlgorithmContext& ctx) const override {
65  py::gil_scoped_acquire acquire{};
66  try {
67  PYBIND11_OVERRIDE_PURE(ProcessCode, IAlgorithm, execute, ctx);
68  } catch (py::error_already_set& e) {
69  throw; // Error from python, handle in python.
70  } catch (std::runtime_error& e) {
71  throw py::type_error("Python algorithm did not conform to interface");
72  }
73  }
74 };
75 
76 void trigger_divbyzero() {
77  volatile float j = 0.0;
78  volatile float r = 123 / j; // MARK: divbyzero
79  (void)r;
80 }
81 
82 void trigger_overflow() {
83  volatile float j = std::numeric_limits<float>::max();
84  volatile float r = j * j; // MARK: overflow
85  (void)r;
86 }
87 
88 void trigger_invalid() {
89  volatile float j = -1;
90  volatile float r = std::sqrt(j); // MARK: invalid
91  (void)r;
92 }
93 
94 } // namespace
95 
96 namespace Acts::Python {
97 void addFramework(Context& ctx) {
98  auto [m, mex] = ctx.get("main", "examples");
99 
100  py::class_<ActsExamples::IWriter, std::shared_ptr<ActsExamples::IWriter>>(
101  mex, "IWriter");
102 
103  py::class_<ActsExamples::IReader, std::shared_ptr<ActsExamples::IReader>>(
104  mex, "IReader");
105 
106  py::enum_<ProcessCode>(mex, "ProcessCode")
107  .value("SUCCESS", ProcessCode::SUCCESS)
108  .value("ABORT", ProcessCode::ABORT)
109  .value("END", ProcessCode::END);
110 
111  py::class_<WhiteBoard>(mex, "WhiteBoard")
113  return std::make_unique<WhiteBoard>(
114  Acts::getDefaultLogger(name, level));
115  }),
116  py::arg("level"), py::arg("name") = "WhiteBoard")
117  .def("exists", &WhiteBoard::exists);
118 
119  py::class_<Acts::GeometryContext>(m, "GeometryContext").def(py::init<>());
120 
121  py::class_<AlgorithmContext>(mex, "AlgorithmContext")
122  .def(py::init<size_t, size_t, WhiteBoard&>())
123  .def_readonly("algorithmNumber", &AlgorithmContext::algorithmNumber)
124  .def_readonly("eventNumber", &AlgorithmContext::eventNumber)
125  .def_property_readonly("eventStore",
126  [](const AlgorithmContext& self) -> WhiteBoard& {
127  return self.eventStore;
128  })
129  .def_readonly("magFieldContext", &AlgorithmContext::magFieldContext)
130  .def_readonly("geoContext", &AlgorithmContext::geoContext)
131  .def_readonly("calibContext", &AlgorithmContext::calibContext)
132  .def_readwrite("fpeMonitor", &AlgorithmContext::fpeMonitor);
133 
134  auto pySequenceElement =
135  py::class_<ActsExamples::SequenceElement, PySequenceElement,
136  std::shared_ptr<ActsExamples::SequenceElement>>(
137  mex, "SequenceElement")
138  .def(py::init_alias<>())
139  .def("internalExecute", &SequenceElement::internalExecute)
140  .def("name", &SequenceElement::name);
141 
142  auto bareAlgorithm =
143  py::class_<ActsExamples::IAlgorithm,
144  std::shared_ptr<ActsExamples::IAlgorithm>, SequenceElement,
145  PyIAlgorithm>(mex, "IAlgorithm")
146  .def(py::init_alias<const std::string&, Acts::Logging::Level>(),
147  py::arg("name"), py::arg("level"))
148  .def("execute", &IAlgorithm::execute);
149 
151  using Config = Sequencer::Config;
152  auto sequencer =
153  py::class_<Sequencer>(mex, "_Sequencer")
154  .def(py::init([](Config cfg) {
155  cfg.iterationCallback = []() {
156  py::gil_scoped_acquire gil;
157  if (PyErr_CheckSignals() != 0) {
158  throw py::error_already_set{};
159  }
160  };
161  return std::make_unique<Sequencer>(cfg);
162  }))
163  .def("run",
164  [](Sequencer& self) {
165  py::gil_scoped_release gil;
166  int res = self.run();
167  if (res != EXIT_SUCCESS) {
168  throw std::runtime_error{"Sequencer terminated abnormally"};
169  }
170  })
171  .def("addContextDecorator", &Sequencer::addContextDecorator)
172  .def("addAlgorithm", &Sequencer::addAlgorithm, py::keep_alive<1, 2>())
173  .def("addReader", &Sequencer::addReader)
174  .def("addWriter", &Sequencer::addWriter)
175  .def("addWhiteboardAlias", &Sequencer::addWhiteboardAlias)
176  .def_property_readonly("config", &Sequencer::config)
177  .def_property_readonly("fpeResult", &Sequencer::fpeResult)
178  .def_property_readonly_static(
179  "_sourceLocation",
180  [](py::object /*self*/) { return std::string{__FILE__}; });
181 
182  auto c = py::class_<Config>(sequencer, "Config").def(py::init<>());
183 
188  ACTS_PYTHON_MEMBER(numThreads);
190  ACTS_PYTHON_MEMBER(outputTimingFile);
191  ACTS_PYTHON_MEMBER(trackFpes);
193  ACTS_PYTHON_MEMBER(failOnFirstFpe);
194  ACTS_PYTHON_MEMBER(fpeStackTraceLength);
196 
197  auto fpem =
198  py::class_<Sequencer::FpeMask>(sequencer, "_FpeMask")
199  .def(py::init<>())
200  .def(py::init<std::string, std::pair<unsigned int, unsigned int>,
201  Acts::FpeType, std::size_t>())
202  .def("__repr__", [](const Sequencer::FpeMask& self) {
203  std::stringstream ss;
204  ss << self;
205  return ss.str();
206  });
207 
214 
215  struct FpeMonitorContext {
216  std::optional<Acts::FpeMonitor> mon;
217  };
218 
219  auto fpe = py::class_<Acts::FpeMonitor>(m, "FpeMonitor")
220  .def_static("_trigger_divbyzero", &trigger_divbyzero)
221  .def_static("_trigger_overflow", &trigger_overflow)
222  .def_static("_trigger_invalid", &trigger_invalid)
223  .def_static("context", []() { return FpeMonitorContext(); });
224 
225  fpe.def_property_readonly("result",
226  py::overload_cast<>(&Acts::FpeMonitor::result),
227  py::return_value_policy::reference_internal)
228  .def("rearm", &Acts::FpeMonitor::rearm);
229 
230  py::class_<Acts::FpeMonitor::Result>(fpe, "Result")
231  .def("merged", &Acts::FpeMonitor::Result::merged)
232  .def("merge", &Acts::FpeMonitor::Result::merge)
233  .def("count", &Acts::FpeMonitor::Result::count)
234  .def("__str__", [](const Acts::FpeMonitor::Result& result) {
235  std::stringstream os;
236  result.summary(os);
237  return os.str();
238  });
239 
240  py::class_<FpeMonitorContext>(m, "_FpeMonitorContext")
241  .def(py::init([]() { return std::make_unique<FpeMonitorContext>(); }))
242  .def(
243  "__enter__",
244  [](FpeMonitorContext& fm) -> Acts::FpeMonitor& {
245  fm.mon.emplace();
246  return fm.mon.value();
247  },
248  py::return_value_policy::reference_internal)
249  .def("__exit__", [](FpeMonitorContext& fm, py::object /*exc_type*/,
250  py::object /*exc_value*/,
251  py::object /*traceback*/) { fm.mon.reset(); });
252 
253  py::enum_<Acts::FpeType>(m, "FpeType")
254  .value("INTDIV", Acts::FpeType::INTDIV)
255  .value("INTOVF", Acts::FpeType::INTOVF)
256  .value("FLTDIV", Acts::FpeType::FLTDIV)
257  .value("FLTOVF", Acts::FpeType::FLTOVF)
258  .value("FLTUND", Acts::FpeType::FLTUND)
259  .value("FLTRES", Acts::FpeType::FLTRES)
260  .value("FLTINV", Acts::FpeType::FLTINV)
261  .value("FLTSUB", Acts::FpeType::FLTSUB)
262 
263  .def_property_readonly_static(
264  "values", [](py::object /*self*/) -> const auto& {
265  static const std::vector<Acts::FpeType> values = {
266  Acts::FpeType::INTDIV, Acts::FpeType::INTOVF,
267  Acts::FpeType::FLTDIV, Acts::FpeType::FLTOVF,
268  Acts::FpeType::FLTUND, Acts::FpeType::FLTRES,
269  Acts::FpeType::FLTINV, Acts::FpeType::FLTSUB};
270  return values;
271  });
272 
273  py::register_exception<ActsExamples::FpeFailure>(m, "FpeFailure",
274  PyExc_RuntimeError);
275 
277  auto randomNumbers =
278  py::class_<RandomNumbers, std::shared_ptr<RandomNumbers>>(mex,
279  "RandomNumbers")
280  .def(py::init<const RandomNumbers::Config&>());
281 
282  py::class_<ActsExamples::RandomEngine>(mex, "RandomEngine").def(py::init<>());
283 
284  py::class_<RandomNumbers::Config>(randomNumbers, "Config")
285  .def(py::init<>())
286  .def_readwrite("seed", &RandomNumbers::Config::seed);
287 }
288 
289 } // namespace Acts::Python