Skip to content

Commit c08d5b4

Browse files
feat: Access to whiteboard from Python (acts-project#5158)
1 parent c7df1d1 commit c08d5b4

File tree

12 files changed

+388
-14
lines changed

12 files changed

+388
-14
lines changed

Core/include/Acts/Utilities/Any.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,6 @@ class AnyBase : public AnyBaseAll {
364364
}
365365
}
366366

367-
template <typename T>
368-
static std::uint64_t typeHash() {
369-
const static std::uint64_t value = detail::fnv1a_64(typeid(T).name());
370-
return value;
371-
}
372-
373367
struct Handler {
374368
void (*destroy)(void* ptr) = nullptr;
375369
void (*moveConstruct)(void* from, void* to) = nullptr;

Core/include/Acts/Utilities/HashedString.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
#include <cstddef>
1313
#include <cstdint>
1414
#include <string_view>
15-
#include <type_traits>
16-
#include <utility>
15+
#include <typeinfo>
1716

1817
namespace Acts {
1918
/// @brief Type alias for hashed string representation
@@ -76,4 +75,15 @@ constexpr HashedString operator""_hash(char const* s, std::size_t count) {
7675
}
7776

7877
} // namespace HashedStringLiteral
78+
79+
/// Hash for a type. Since it's not possible to hash a type at compile-time,
80+
/// this function returns a runtime hash but caches it in a static variable.
81+
/// @tparam T Type to hash
82+
/// @return Hashed string representation
83+
template <typename T>
84+
std::uint64_t typeHash() {
85+
const static std::uint64_t value = detail::fnv1a_64(typeid(T).name());
86+
return value;
87+
}
88+
7989
} // namespace Acts

Examples/Framework/include/ActsExamples/Framework/DataHandle.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#pragma once
1010

11+
#include "Acts/Utilities/HashedString.hpp"
1112
#include "ActsExamples/Framework/AlgorithmContext.hpp"
1213
#include "ActsExamples/Framework/SequenceElement.hpp"
1314
#include "ActsExamples/Framework/WhiteBoard.hpp"
@@ -51,6 +52,7 @@ class DataHandleBase {
5152
const std::string& key() const { return m_key.value(); }
5253

5354
virtual const std::type_info& typeInfo() const = 0;
55+
virtual std::uint64_t typeHash() const = 0;
5456

5557
bool isInitialized() const { return m_key.has_value(); }
5658

@@ -88,6 +90,10 @@ class DataHandleBase {
8890
return wb.pop<T>(m_key.value());
8991
}
9092

93+
WhiteBoard::IHolder* getHolder(const WhiteBoard& wb) const {
94+
return wb.getHolder(m_key.value());
95+
}
96+
9197
SequenceElement* m_parent{nullptr};
9298
std::string m_name;
9399
std::optional<std::string> m_key{};
@@ -180,6 +186,7 @@ class WriteDataHandle final : public WriteDataHandleBase {
180186
}
181187

182188
const std::type_info& typeInfo() const override { return typeid(T); };
189+
std::uint64_t typeHash() const override { return Acts::typeHash<T>(); };
183190
};
184191

185192
/// A read handle for accessing data from the WhiteBoard.
@@ -213,6 +220,7 @@ class ReadDataHandle final : public ReadDataHandleBase {
213220
}
214221

215222
const std::type_info& typeInfo() const override { return typeid(T); };
223+
std::uint64_t typeHash() const override { return Acts::typeHash<T>(); };
216224
};
217225

218226
/// A consume handle for taking ownership of data from the WhiteBoard.
@@ -247,6 +255,7 @@ class ConsumeDataHandle final : public ConsumeDataHandleBase {
247255
}
248256

249257
const std::type_info& typeInfo() const override { return typeid(T); };
258+
std::uint64_t typeHash() const override { return Acts::typeHash<T>(); };
250259
};
251260

252261
} // namespace ActsExamples

Examples/Framework/include/ActsExamples/Framework/WhiteBoard.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#pragma once
1010

1111
#include "Acts/Utilities/Concepts.hpp"
12+
#include "Acts/Utilities/HashedString.hpp"
1213
#include "Acts/Utilities/Logger.hpp"
1314

1415
#include <algorithm>
@@ -36,13 +37,17 @@ class WhiteBoard {
3637
struct IHolder {
3738
virtual ~IHolder() = default;
3839
virtual const std::type_info& type() const = 0;
40+
virtual const void* data() const = 0;
41+
virtual std::uint64_t typeHash() const = 0;
3942
};
4043
template <Acts::Concepts::nothrow_move_constructible T>
4144
struct HolderT : public IHolder {
4245
T value;
4346

4447
explicit HolderT(T&& v) : value(std::move(v)) {}
4548
const std::type_info& type() const override { return typeid(T); }
49+
std::uint64_t typeHash() const override { return Acts::typeHash<T>(); }
50+
const void* data() const override { return std::addressof(value); }
4651
};
4752

4853
struct StringHash {
@@ -116,6 +121,8 @@ class WhiteBoard {
116121
template <typename T>
117122
HolderT<T>* getHolder(const std::string& name) const;
118123

124+
IHolder* getHolder(const std::string& name) const;
125+
119126
template <typename T>
120127
T pop(const std::string& name);
121128

Examples/Framework/src/Framework/DataHandle.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "ActsExamples/Framework/Sequencer.hpp"
1313

1414
#include <regex>
15+
#include <typeindex>
16+
#include <typeinfo>
1517

1618
#include <boost/algorithm/string.hpp>
1719
#include <boost/core/demangle.hpp>
@@ -73,8 +75,7 @@ void DataHandleBase::maybeInitialize(std::optional<std::string_view> key) {
7375
}
7476

7577
bool WriteDataHandleBase::isCompatible(const DataHandleBase& other) const {
76-
return dynamic_cast<const ReadDataHandleBase*>(&other) != nullptr &&
77-
typeInfo() == other.typeInfo();
78+
return typeHash() == other.typeHash();
7879
}
7980

8081
void WriteDataHandleBase::emulate(StateMapType& state,
@@ -114,8 +115,7 @@ void ReadDataHandleBase::initialize(std::string_view key) {
114115
}
115116

116117
bool ReadDataHandleBase::isCompatible(const DataHandleBase& other) const {
117-
return dynamic_cast<const WriteDataHandleBase*>(&other) != nullptr &&
118-
typeInfo() == other.typeInfo();
118+
return typeHash() == other.typeHash();
119119
}
120120

121121
void ReadDataHandleBase::emulate(StateMapType& state,

Examples/Framework/src/Framework/WhiteBoard.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,12 @@ std::vector<std::string> WhiteBoard::getKeys() const {
136136
return keys;
137137
}
138138

139+
WhiteBoard::IHolder *WhiteBoard::getHolder(const std::string &name) const {
140+
auto it = m_store.find(name);
141+
if (it == m_store.end()) {
142+
throw std::out_of_range("Object '" + name + "' does not exists");
143+
}
144+
return it->second.get();
145+
}
146+
139147
} // namespace ActsExamples

Examples/Io/Podio/include/ActsExamples/Io/Podio/CollectionBaseWriteHandle.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class CollectionBaseWriteHandle : public WriteDataHandleBase {
7575
/// Get the type info for this handle
7676
/// @return The type info for std::unique_ptr<podio::CollectionBase>
7777
const std::type_info& typeInfo() const override;
78+
79+
std::uint64_t typeHash() const override;
7880
};
7981

8082
} // namespace ActsExamples

Examples/Io/Podio/src/CollectionBaseWriteHandle.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ const std::type_info& CollectionBaseWriteHandle::typeInfo() const {
3636
return typeid(std::unique_ptr<podio::CollectionBase>);
3737
}
3838

39+
std::uint64_t CollectionBaseWriteHandle::typeHash() const {
40+
return Acts::typeHash<std::unique_ptr<podio::CollectionBase>>();
41+
}
42+
3943
} // namespace ActsExamples

Python/Examples/src/Framework.cpp

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "Acts/Utilities/Logger.hpp"
1010
#include "ActsExamples/Framework/AlgorithmContext.hpp"
11+
#include "ActsExamples/Framework/DataHandle.hpp"
1112
#include "ActsExamples/Framework/IAlgorithm.hpp"
1213
#include "ActsExamples/Framework/IReader.hpp"
1314
#include "ActsExamples/Framework/IWriter.hpp"
@@ -16,9 +17,12 @@
1617
#include "ActsExamples/Framework/SequenceElement.hpp"
1718
#include "ActsExamples/Framework/Sequencer.hpp"
1819
#include "ActsExamples/Framework/WhiteBoard.hpp"
19-
#include "ActsPython/Utilities/Helpers.hpp"
2020
#include "ActsPython/Utilities/Macros.hpp"
21+
#include "ActsPython/Utilities/WhiteBoardTypeRegistry.hpp"
2122

23+
#include <stdexcept>
24+
25+
#include <boost/core/demangle.hpp>
2226
#include <pybind11/pybind11.h>
2327
#include <pybind11/stl.h>
2428

@@ -82,11 +86,72 @@ class PyIAlgorithm : public IAlgorithm {
8286
}
8387
}
8488

89+
ProcessCode initialize() override {
90+
py::gil_scoped_acquire acquire{};
91+
PYBIND11_OVERRIDE(ProcessCode, IAlgorithm, initialize, );
92+
}
93+
94+
ProcessCode finalize() override {
95+
py::gil_scoped_acquire acquire{};
96+
PYBIND11_OVERRIDE(ProcessCode, IAlgorithm, finalize, );
97+
}
98+
8599
std::string_view typeName() const override { return "Algorithm"; }
86100

87101
const Acts::Logger& pyLogger() const { return logger(); }
88102
};
89103

104+
class PyReadDataHandle : public ReadDataHandleBase {
105+
public:
106+
PyReadDataHandle(SequenceElement* parent, py::object pytype,
107+
const std::string& name)
108+
: ReadDataHandleBase(parent, name) {
109+
m_entry = WhiteBoardRegistry::find(pytype);
110+
if (m_entry == nullptr) {
111+
throw py::type_error("Type '" +
112+
pytype.attr("__qualname__").cast<std::string>() +
113+
"' is not registered for WhiteBoard access");
114+
}
115+
if (m_entry->typeinfo == nullptr) {
116+
throw py::type_error("Type '" +
117+
pytype.attr("__qualname__").cast<std::string>() +
118+
"' is not registered for WhiteBoard access");
119+
}
120+
121+
registerAsReadHandle();
122+
}
123+
124+
const std::type_info& typeInfo() const override { return *m_entry->typeinfo; }
125+
126+
std::uint64_t typeHash() const override { return m_entry->typeHash; }
127+
128+
py::object call(const py::object& wbPy) const {
129+
if (!isInitialized()) {
130+
throw std::runtime_error("ReadDataHandle '" + name() +
131+
"' not initialized");
132+
}
133+
const auto& wb = wbPy.cast<const ActsExamples::WhiteBoard&>();
134+
135+
if (!wb.exists(key())) {
136+
throw py::key_error("Key '" + key() + "' does not exist");
137+
}
138+
139+
const auto& holder = getHolder(wb);
140+
141+
if (m_entry->typeHash != holder->typeHash()) {
142+
const auto& expected = boost::core::demangle(m_entry->typeinfo->name());
143+
const auto& actual = boost::core::demangle(holder->type().name());
144+
throw py::type_error("Type mismatch for key '" + key() + "'. Expected " +
145+
expected + " but got " + actual);
146+
}
147+
148+
return m_entry->fn(holder->data(), wbPy);
149+
}
150+
151+
private:
152+
const WhiteBoardRegistry::RegistryEntry* m_entry{nullptr};
153+
};
154+
90155
void trigger_divbyzero() {
91156
volatile float j = 0.0;
92157
volatile float r = 123 / j; // MARK: divbyzero
@@ -132,6 +197,23 @@ void addFramework(py::module& mex) {
132197
.def("exists", &WhiteBoard::exists)
133198
.def_property_readonly("keys", &WhiteBoard::getKeys);
134199

200+
py::class_<PyReadDataHandle>(mex, "ReadDataHandle")
201+
.def(py::init([](const py::object& parent_py, py::object pytype,
202+
const std::string& name) {
203+
auto* parent = parent_py.cast<SequenceElement*>();
204+
return std::make_unique<PyReadDataHandle>(parent,
205+
std::move(pytype), name);
206+
}),
207+
py::arg("parent"), py::arg("type"), py::arg("name"),
208+
py::keep_alive<1, 2>())
209+
.def(
210+
"initialize",
211+
[](PyReadDataHandle& self, std::string_view key) {
212+
self.initialize(key);
213+
},
214+
py::arg("key"))
215+
.def("__call__", &PyReadDataHandle::call, py::arg("whiteboard"));
216+
135217
py::class_<AlgorithmContext>(mex, "AlgorithmContext")
136218
.def(py::init<std::size_t, std::size_t, WhiteBoard&, std::size_t>(),
137219
"alg"_a, "event"_a, "store"_a, "thread"_a)

Python/Examples/src/Generators.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ActsExamples/Utilities/ParametricParticleGenerator.hpp"
1515
#include "ActsExamples/Utilities/VertexGenerators.hpp"
1616
#include "ActsPython/Utilities/Macros.hpp"
17+
#include "ActsPython/Utilities/WhiteBoardTypeRegistry.hpp"
1718

1819
#include <cstddef>
1920
#include <memory>
@@ -136,7 +137,15 @@ void addGenerators(py::module& mex) {
136137
.def_readwrite("fixed", &FixedPrimaryVertexPositionGenerator::fixed);
137138

138139
py::class_<SimParticle>(mex, "SimParticle");
139-
py::class_<SimParticleContainer>(mex, "SimParticleContainer");
140+
auto simParticleContainer =
141+
py::class_<SimParticleContainer>(mex, "SimParticleContainer")
142+
.def("__len__",
143+
[](const SimParticleContainer& self) { return self.size(); })
144+
.def("__iter__", [](const SimParticleContainer& self) {
145+
return py::make_iterator(self.begin(), self.end());
146+
});
147+
148+
WhiteBoardRegistry::registerClass(simParticleContainer);
140149

141150
{
142151
using Config = ParametricParticleGenerator::Config;

0 commit comments

Comments
 (0)