ATLAS Offline Software
Loading...
Searching...
No Matches
NanobindBindings.cxx File Reference
#include <ColumnarToolWrapperPython/PythonToolHandle.h>
#include <ColumnarCore/ColumnarDef.h>
#include <ColumnarCore/ColumnInfoHelpers.h>
#include <CxxUtils/crc64.h>
#include <AsgMessaging/MsgLevel.h>
#include <nanobind/nanobind.h>
#include <nanobind/ndarray.h>
#include <nanobind/operators.h>
#include <nanobind/stl/list.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/pair.h>
#include <nanobind/stl/map.h>
#include <cstdio>
#include <string>

Go to the source code of this file.

Macros

#define MAKE_NDARRAY(T)

Functions

std::string getAddressString (const columnar::PythonToolHandle &obj)
std::string get_type_name (const std::type_info &type_info)
void setProperty (columnar::PythonToolHandle &self, const std::string &key, nb::object value)
nb::object getColumnVoid (columnar::PythonToolHandle &self, const std::string &key)
void setColumnVoid (columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column, bool is_const=true)
void setImmutableColumnVoid (columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column)
 NB_MODULE (python_tool_handle, module)

Macro Definition Documentation

◆ MAKE_NDARRAY

#define MAKE_NDARRAY ( T)
Value:
nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()
size_t size() const
Number of registered mappings.

Function Documentation

◆ get_type_name()

std::string get_type_name ( const std::type_info & type_info)

escape hatch for char

Definition at line 45 of file NanobindBindings.cxx.

45 {
46 if (type_info == typeid(int)) {
47 return "int32";
48 } else if (type_info == typeid(unsigned int)) {
49 return "uint32";
50 } else if (type_info == typeid(short)) {
51 return "int16";
52 } else if (type_info == typeid(unsigned short)) {
53 return "uint16";
54 } else if (type_info == typeid(char)) {
56 return "int8";
57 } else if (type_info == typeid(unsigned char)) {
58 return "uint8";
59 } else if (type_info == typeid(float)) {
60 return "float32";
61 } else if (type_info == typeid(double)) {
62 return "float64";
63 } else if (type_info == typeid(long)) {
64 return "int64";
65 } else if (type_info == typeid(unsigned long)) {
66 return "uint64";
67 } else if (type_info == typeid(bool)) {
68 return "bool";
69 } else {
70 // If the type is unknown, you can return the mangled name or a default message
71 return std::string("unknown ('") + type_info.name() + "')";
72 }
73}

◆ getAddressString()

std::string getAddressString ( const columnar::PythonToolHandle & obj)

Definition at line 38 of file NanobindBindings.cxx.

38 {
39 char buffer[32]; // Ensure the buffer is large enough for the hex representation
40 std::snprintf(buffer, sizeof(buffer), "0x%llx", reinterpret_cast<unsigned long long>(&obj));
41 return std::string(buffer);
42}

◆ getColumnVoid()

nb::object getColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key )

Definition at line 87 of file NanobindBindings.cxx.

87 {
88 auto [size, ptr, type] = self.getColumnVoid(key);
89 // Wrap the raw pointer in a numpy array without copying. The caller is
90 // responsible for keeping the PythonToolHandle alive while using the result.
91#define MAKE_NDARRAY(T) \
92 nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()
93 if (*type == typeid(float)) return MAKE_NDARRAY(float);
94 if (*type == typeid(double)) return MAKE_NDARRAY(double);
95 if (*type == typeid(char)) return MAKE_NDARRAY(char);
96 if (*type == typeid(int)) return MAKE_NDARRAY(int);
97 if (*type == typeid(std::uint8_t)) return MAKE_NDARRAY(std::uint8_t);
98 if (*type == typeid(std::uint16_t)) return MAKE_NDARRAY(std::uint16_t);
99 if (*type == typeid(std::uint32_t)) return MAKE_NDARRAY(std::uint32_t);
100 if (*type == typeid(std::uint64_t)) return MAKE_NDARRAY(std::uint64_t);
101 if (*type == typeid(std::int16_t)) return MAKE_NDARRAY(std::int16_t);
102 if (*type == typeid(std::int32_t)) return MAKE_NDARRAY(std::int32_t);
103 if (*type == typeid(std::int64_t)) return MAKE_NDARRAY(std::int64_t);
104#undef MAKE_NDARRAY
105 throw std::runtime_error("getColumnVoid: unsupported column type: " + std::string(type->name()));
106}
#define MAKE_NDARRAY(T)
std::tuple< std::size_t, const void *, const std::type_info * > getColumnVoid(const std::string &name) const
get a column pointer by name; returns {size, ptr, type_info}
void * ptr(T *p)
Definition SGImplSvc.cxx:74

◆ NB_MODULE()

NB_MODULE ( python_tool_handle ,
module  )

load in the ColumnAccessMode enum

Definition at line 195 of file NanobindBindings.cxx.

195 {
196 module.doc() = "Nanobind bindings for PythonToolHandle";
197
199 throw nb::import_error("This module can only be used in columnar access mode. Try setting up a ColumnarAnalysis release instead.");
200
201 module.attr("numberOfEventsName") = &columnar::eventRangeColumnName;
202 module.attr("eventRangeColumnName") = &columnar::eventRangeColumnName;
203
204 // the value used for an invalid (null/empty) element link in array mode
205 module.attr("invalid_link_value") = columnar::ColumnarModeArray::invalidLinkValue;
206
207 module.def("crc64",
208 [](const std::string& data) { return CxxUtils::crc64(data); },
209 "data"_a,
210 "CRC-64 of a string, using the Athena default polynomial "
211 "(CxxUtils::crc64).");
212
213 module.def("crc64addint", &CxxUtils::crc64addint,
214 "crc"_a, "x"_a,
215 "Extend a previously-calculated CRC-64 to include an integer "
216 "(CxxUtils::crc64addint).");
217
218 module.def("sg_key",
219 &columnar::computeSgKey,
220 "name"_a, "clid"_a = 0,
221 "StoreGate hash (sgkey) of a container name, optionally mixed with "
222 "its CLID. ElementLink m_persKey values in Athena-written files are "
223 "sg_key(container_name, container_clid).");
224
225 // Install a Python callable as the global C++ message printer.
226#ifdef XAOD_STANDALONE
227 // Overload 1: with callback function
228 module.def("set_python_printer", &set_printer_from_callable, nb::arg("callback"),
229 "Install a Python callable(level: int, name: str, text: str) as the global "
230 "C++ message printer.");
231
232 // Overload 2: reset (no argument)
233 module.def("set_python_printer", &clear_printer,
234 "Reset to the default stdout message printer.");
235#else
236 // In Athena/AthAnalysis builds IMessagePrinter does not exist; expose the
237 // function so Python code doesn't get AttributeError, but raise at call time.
238 module.def("set_python_printer", [](nb::args, nb::kwargs) {
239 throw std::runtime_error(
240 "set_python_printer is only available in standalone "
241 "(AnalysisBase/ColumnarAnalysis) builds, not in Athena/AthAnalysis.");
242 }, "Not available in Athena/AthAnalysis builds.");
243#endif // XAOD_STANDALONE
244
246 nb::enum_<columnar::ColumnAccessMode>(module, "ColumnAccessMode")
247 .value("input", columnar::ColumnAccessMode::input)
248 .value("output", columnar::ColumnAccessMode::output)
249 .value("update", columnar::ColumnAccessMode::update)
250 .def("__repr__", [](const columnar::ColumnAccessMode &mode) -> std::string {
251 switch (mode) {
253 return "<ColumnAccessMode input>";
255 return "<ColumnAccessMode output>";
257 return "<ColumnAccessMode update>";
258 default:
259 return "<ColumnAccessMode update value=" + std::to_string(static_cast<int>(mode)) + ">";
260 }
261 })
262 .export_values(); // Makes the enum values accessible without namespace in Python
263
264 nb::enum_<MSG::Level>(module, "MsgLevel", nb::is_arithmetic())
265 .value("NIL", MSG::NIL)
266 .value("VERBOSE", MSG::VERBOSE)
267 .value("DEBUG", MSG::DEBUG)
268 .value("INFO", MSG::INFO)
269 .value("WARNING", MSG::WARNING)
270 .value("ERROR", MSG::ERROR)
271 .value("FATAL", MSG::FATAL)
272 .export_values();
273
274 nb::class_<columnar::ColumnInfo>(module, "ColumnInfo")
275 .def(nb::init<>()) // Default constructor
276 .def_ro("name", &columnar::ColumnInfo::name)
277 .def_ro("index", &columnar::ColumnInfo::index)
278 .def_prop_ro("dtype", [](const columnar::ColumnInfo &self){
279 return get_type_name(*self.type);
280 })
281 .def_ro("access_mode", &columnar::ColumnInfo::accessMode)
282 .def_ro("offset_name", &columnar::ColumnInfo::offsetName)
283 .def_ro("fixed_dimensions", &columnar::ColumnInfo::fixedDimensions)
284 .def_ro("sole_link_target_name", &columnar::ColumnInfo::soleLinkTargetName)
285 .def_ro("sole_link_target_clid", &columnar::ColumnInfo::soleLinkTargetClid)
286 .def_ro("is_variant_link", &columnar::ColumnInfo::isVariantLink)
287 .def_ro("variant_link_target_names", &columnar::ColumnInfo::variantLinkTargetNames)
288 .def_ro("key_column_for_variant_link", &columnar::ColumnInfo::keyColumnForVariantLink)
289 .def_ro("is_offset", &columnar::ColumnInfo::isOffset)
290 .def_ro("replaces_column", &columnar::ColumnInfo::replacesColumn)
291 .def_ro("is_optional", &columnar::ColumnInfo::isOptional)
292 .def("__repr__", [](const columnar::ColumnInfo &self) {
293 std::string access_mode;
294 switch (self.accessMode) {
296 access_mode = "input";
297 break;
299 access_mode = "output";
300 break;
302 access_mode = "update";
303 break;
304 default:
305 // For unknown values, return the integer value
306 access_mode = "unknown";
307 }
308 return "<ColumnInfo name='" + self.name + "'" +
309 (self.isOffset ? "" : ", offset='" + self.offsetName + "'") +
310 ", access_mode='" + access_mode + "'" +
311 ", dtype='" + (self.type ? get_type_name(*self.type) : "" ) + "'" +
312 (self.isOptional ? ", optional": "") +
313 ">";
314 })
315 .def("to_dict", [](const columnar::ColumnInfo& self) {
316 nb::dict d;
317 d["name"] = self.name;
318 d["index"] = self.index;
319 d["dtype"] = self.type ? get_type_name(*self.type) : "";
320 d["access_mode"] = static_cast<int>(self.accessMode);
321 d["offset_name"] = self.offsetName;
322 d["fixed_dimensions"] = self.fixedDimensions;
323 d["sole_link_target_name"] = self.soleLinkTargetName;
324 d["sole_link_target_clid"] = self.soleLinkTargetClid;
325 d["is_variant_link"] = self.isVariantLink;
326 d["variant_link_target_names"] = self.variantLinkTargetNames;
327 d["key_column_for_variant_link"] = self.keyColumnForVariantLink;
328 d["is_offset"] = self.isOffset;
329 d["replaces_column"] = self.replacesColumn;
330 d["is_optional"] = self.isOptional;
331 return d;
332 });
333
334 nb::class_<columnar::PythonToolHandle>(module, "PythonToolHandle")
335 .def(nb::init())
336
337 // Properties
338 .def_prop_ro("type", [](const columnar::PythonToolHandle &self) {
339 const asg::AsgToolConfig& config = self.getConfig();
340 const std::string& type = config.type();
341 if (type.empty()) {
342 std::cerr << "Warning: PythonToolHandle.type is empty."
343 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
344 }
345 return type;
346 })
347
348 .def_prop_ro("name", [](const columnar::PythonToolHandle &self) {
349 const asg::AsgToolConfig& config = self.getConfig();
350 const std::string& name = config.name();
351 if (name.empty()) {
352 std::cerr << "Warning: PythonToolHandle.name is empty."
353 << " Set with PythonToolHandle.set_type_and_name." << std::endl;
354 }
355 return name;
356 })
357
358 // Methods
359 .def("set_type_and_name",
360 [](columnar::PythonToolHandle &self, const std::string& type_and_name) {
361 self.setTypeAndName(type_and_name);
362 },
363 "type_and_name"_a,
364 "Set the type and name of the tool.")
365
366 .def("set_property", &setProperty,
367 "key"_a, "value"_a,
368 "Set a property on the tool.")
369
370 .def("__setattr__", &setProperty,
371 "key"_a, "value"_a,
372 "Set a property on the tool.")
373
374 .def("preinitialize",
376 "Preinitialize the tool.")
377
378 // rename_containers([("from", "to"), ...])
379 .def("rename_containers",
381 "renames"_a,
382 "Rename the columns the tool uses.")
383
384 // rename_containers({"from": "to"}, ...})
385 .def("rename_containers",
386 [](columnar::PythonToolHandle &self, const std::map<std::string,std::string>& renames){
387 std::vector<std::pair<std::string, std::string>> vectorized;
388 for (const auto& pair : renames)
389 vectorized.emplace_back(pair);
390
391 return self.renameContainers(vectorized);
392 },
393 "renames"_a,
394 "Rename the columns the tool uses.")
395
396 .def("initialize",
398 "Initialize the tool.")
399
400 .def("apply_systematic_variation",
401 [](columnar::PythonToolHandle &self, const std::string& sys_name) {
402 self.applySystematicVariation(sys_name);
403 },
404 "sys_name"_a,
405 "Apply a systematic variation to the tool.")
406
407 .def("set_column",
408 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<float> column) {
409 self.setColumn<float>(key, column.shape(0), column.data());
410 },
411 "key"_a, "column"_a,
412 "Set a float column pointer.")
413
414 .def("set_column",
415 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<char> column) {
416 self.setColumn<char>(key, column.shape(0), column.data());
417 },
418 "key"_a, "column"_a,
419 "Set a char column pointer.")
420
421 .def("set_column",
422 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<int> column) {
423 self.setColumn<int>(key, column.shape(0), column.data());
424 },
425 "key"_a, "column"_a,
426 "Set an int column pointer.")
427
428 .def("set_column",
429 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint8_t> column) {
430 self.setColumn<uint8_t>(key, column.shape(0), column.data());
431 },
432 "key"_a, "column"_a,
433 "Set a uint8_t column pointer.")
434
435 .def("set_column",
436 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint16_t> column) {
437 self.setColumn<uint16_t>(key, column.shape(0), column.data());
438 },
439 "key"_a, "column"_a,
440 "Set a uint16_t column pointer.")
441
442 .def("set_column",
443 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint32_t> column) {
444 self.setColumn<uint32_t>(key, column.shape(0), column.data());
445 },
446 "key"_a, "column"_a,
447 "Set a uint32_t column pointer.")
448
449 .def("set_column",
450 [](columnar::PythonToolHandle &self, const std::string& key, nb::ndarray<std::uint64_t> column) {
451 self.setColumn<uint64_t>(key, column.shape(0), column.data());
452 },
453 "key"_a, "column"_a,
454 "Set a uint64_t column pointer.")
455
456 .def("set_column_void", &setColumnVoid,
457 // cppcheck-suppress assignBoolToPointer
458 "key"_a, "column"_a, "is_const"_a = true,
459 "Set a void column pointer (nanobind version).")
460
461 .def("__setitem__", &setImmutableColumnVoid,
462 "key"_a, "column"_a,
463 "Set a void immutable column pointer (nanobind version).")
464
465 .def("__getitem__", &getColumnVoid,
466 "key"_a,
467 "Get a column as a numpy array (zero-copy view into the tool's buffer).")
468
469 .def("keys",
471 "Return the column names (enables dict(handle)).")
472
473 .def("call",
475 "Call the tool and reset the columns.")
476
477 .def_prop_ro(
478 "columns",
480 "Get the expected column information."
481 )
482
483 .def("get_recommended_systematics",
485 "Get the recommended systematics.")
486
487 // Make this more fancy in the future
488 // <PythonToolHandle(CP::MuonEfficiencyScaleFactors/unique0) object at 0x7f2943b07568>
489 .def("__repr__", [](const columnar::PythonToolHandle &self) {
490 const asg::AsgToolConfig& config = self.getConfig();
491 return "<PythonToolHandle(" + config.type() + "/" + config.name() + ") object at " + getAddressString(self) + ">";
492 });
493}
nb::object getColumnVoid(columnar::PythonToolHandle &self, const std::string &key)
std::string get_type_name(const std::type_info &type_info)
void setColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column, bool is_const=true)
void setProperty(columnar::PythonToolHandle &self, const std::string &key, nb::object value)
void setImmutableColumnVoid(columnar::PythonToolHandle &self, const std::string &key, nb::ndarray<> column)
std::string getAddressString(const columnar::PythonToolHandle &obj)
an object that can create a AsgTool
a handle to a python tool for use via nanobind
void setColumn(const std::string &key, std::size_t size, CT *dataPtr)
set a column pointer (raw pointer version)
void renameContainers(const std::vector< std::pair< std::string, std::string > > &renames)
rename the columns the tool uses
void preinitialize()
preinitialize the tool
void applySystematicVariation(const std::string &sysName)
set the tool to apply the given systematic variation
void setTypeAndName(const std::string &typeAndName)
set the type and name for the tool
std::vector< std::string > getRecommendedSystematics() const
get the recommended systematics
const asg::AsgToolConfig & getConfig() const
get the AsgToolConfig
std::vector< ColumnInfo > getColumnInfo() const
get the expected column info
std::vector< std::string > getColumnNames() const
get the expected column names
void call()
call the tool and reset the columns
void initialize()
initialize the tool
STL class.
constexpr unsigned columnarAccessMode
ColumnAccessMode
an enum for the different access modes for a column
Definition ColumnInfo.h:20
@ update
an updateable column
Definition ColumnInfo.h:28
@ output
an output column
Definition ColumnInfo.h:25
@ input
an input column
Definition ColumnInfo.h:22
setWord1 uint16_t
setEventNumber uint32_t
a struct that contains meta-information about each column that's needed to interface the column with ...
Definition ColumnInfo.h:36
std::string offsetName
the name of the offset column used for this column (or empty string for none)
Definition ColumnInfo.h:75
std::string soleLinkTargetName
for simple link columns: the name of the target container
Definition ColumnInfo.h:132
std::string keyColumnForVariantLink
if this is a key column for a variant link, the name of the associated link column
Definition ColumnInfo.h:192
std::uint32_t soleLinkTargetClid
for simple link columns: the CLID of the target container
Definition ColumnInfo.h:146
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
Definition ColumnInfo.h:83
bool isOptional
whether this column is optional
Definition ColumnInfo.h:122
std::vector< std::string > variantLinkTargetNames
for variant link key columns: the names of the containers we can link to
Definition ColumnInfo.h:177
std::string name
the name of the column
Definition ColumnInfo.h:43
bool isOffset
whether this is an offset column
Definition ColumnInfo.h:93
ColumnAccessMode accessMode
the access mode for the column
Definition ColumnInfo.h:59
std::string replacesColumn
whether this replaces another column
Definition ColumnInfo.h:103
bool isVariantLink
whether this is a variant link column
Definition ColumnInfo.h:154
unsigned index
the index of the column in the data array
Definition ColumnInfo.h:47
const std::type_info * type
the type of the individual entries in the column
Definition ColumnInfo.h:55

◆ setColumnVoid()

void setColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key,
nb::ndarray<> column,
bool is_const = true )

Definition at line 108 of file NanobindBindings.cxx.

108 {
109 // TODO: figure out how to get type_info from handle instead...
110 // nb::handle handle = column.handle();
111
112 const std::type_info* type_info = nullptr;
113 const nb::dlpack::dtype dtype = column.dtype();
114 switch ((nb::dlpack::dtype_code) dtype.code) {
115 case nb::dlpack::dtype_code::Int:
116 switch (dtype.bits) {
117 // escape hatch to handle char for now
118 // we should rely on signed/unsigned and nbits, instead of std::type_info
119 // case 8: type_info = &typeid(std::int8_t); break;
120 case 8: type_info = &typeid(char); break;
121 case 16: type_info = &typeid(std::int16_t); break;
122 case 32: type_info = &typeid(std::int32_t); break;
123 case 64: type_info = &typeid(std::int64_t); break;
124 }
125 break;
126
127 case nb::dlpack::dtype_code::UInt:
128 switch (dtype.bits) {
129 case 8: type_info = &typeid(std::uint8_t); break;
130 case 16: type_info = &typeid(std::uint16_t); break;
131 case 32: type_info = &typeid(std::uint32_t); break;
132 case 64: type_info = &typeid(std::uint64_t); break;
133 }
134 break;
135
136 case nb::dlpack::dtype_code::Float:
137 switch (dtype.bits) {
138 case 32: type_info = &typeid(float); break;
139 case 64: type_info = &typeid(double); break;
140 }
141 break;
142
143 default:
144 break;
145 }
146
147 if (type_info == nullptr) throw std::runtime_error ("unsupported column type passed in");
148 self.setColumnVoid(key, column.shape(0), column.data(), *type_info, is_const);
149}
void setColumnVoid(const std::string &name, std::size_t size, const void *dataPtr, const std::type_info &type, bool isConst)
set a column pointer

◆ setImmutableColumnVoid()

void setImmutableColumnVoid ( columnar::PythonToolHandle & self,
const std::string & key,
nb::ndarray<> column )

Definition at line 151 of file NanobindBindings.cxx.

151 {
152 setColumnVoid(self, key, column, true);
153};

◆ setProperty()

void setProperty ( columnar::PythonToolHandle & self,
const std::string & key,
nb::object value )

Definition at line 75 of file NanobindBindings.cxx.

75 {
76 if (nb::isinstance<nb::str>(value)) {
77 self.setProperty(key, nb::cast<std::string>(value));
78 } else if (nb::isinstance<nb::int_>(value)) {
79 self.setProperty(key, nb::cast<int>(value));
80 } else if (nb::isinstance<nb::float_>(value)) {
81 self.setProperty(key, nb::cast<double>(value));
82 } else {
83 throw std::runtime_error("Unsupported property type. Must be str, int, or float.");
84 }
85}
void setProperty(const std::string &key, T &&value)
set a property on the tool