20#include <nanobind/nanobind.h>
21#include <nanobind/ndarray.h>
22#include <nanobind/operators.h>
23#include <nanobind/stl/list.h>
24#include <nanobind/stl/string.h>
25#include <nanobind/stl/vector.h>
26#include <nanobind/stl/pair.h>
27#include <nanobind/stl/map.h>
32namespace nb = nanobind;
34using namespace nb::literals;
40 std::snprintf(buffer,
sizeof(buffer),
"0x%llx",
reinterpret_cast<unsigned long long>(&obj));
41 return std::string(buffer);
46 if (type_info ==
typeid(
int)) {
48 }
else if (type_info ==
typeid(
unsigned int)) {
50 }
else if (type_info ==
typeid(
short)) {
52 }
else if (type_info ==
typeid(
unsigned short)) {
54 }
else if (type_info ==
typeid(
char)) {
57 }
else if (type_info ==
typeid(
unsigned char)) {
59 }
else if (type_info ==
typeid(
float)) {
61 }
else if (type_info ==
typeid(
double)) {
63 }
else if (type_info ==
typeid(
long)) {
65 }
else if (type_info ==
typeid(
unsigned long)) {
67 }
else if (type_info ==
typeid(
bool)) {
71 return std::string(
"unknown ('") + type_info.name() +
"')";
76 if (nb::isinstance<nb::str>(value)) {
77 self.
setProperty(key, nb::cast<std::string>(value));
78 }
else if (nb::isinstance<nb::int_>(value)) {
80 }
else if (nb::isinstance<nb::float_>(value)) {
83 throw std::runtime_error(
"Unsupported property type. Must be str, int, or float.");
91#define MAKE_NDARRAY(T) \
92 nb::ndarray<nb::numpy, T, nb::ro>(static_cast<const T*>(ptr), {size}).cast()
105 throw std::runtime_error(
"getColumnVoid: unsupported column type: " + std::string(
type->name()));
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) {
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;
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;
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;
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);
157#ifdef XAOD_STANDALONE
160struct PyMessagePrinter :
public asg::IMessagePrinter {
161 nb::callable m_callback;
163 explicit PyMessagePrinter(nb::callable cb)
164 : m_callback(std::move(cb)) {}
166 void print(MSG::Level lvl,
const std::string& name,
167 const std::string& text)
override {
169 if (!Py_IsInitialized())
171 nb::gil_scoped_acquire gil;
172 m_callback(
static_cast<int>(lvl), name, text);
177static std::unique_ptr<PyMessagePrinter> g_printer;
178static std::unique_ptr<asg::MessagePrinterOverlay> g_overlay;
180void set_printer_from_callable(nb::callable cb) {
181 g_printer = std::make_unique<PyMessagePrinter>(std::move(cb));
183 g_overlay = std::make_unique<asg::MessagePrinterOverlay>(g_printer.get());
186void clear_printer() {
196 module.doc() = "Nanobind bindings for PythonToolHandle";
199 throw nb::import_error(
"This module can only be used in columnar access mode. Try setting up a ColumnarAnalysis release instead.");
201 module.attr("numberOfEventsName") = &columnar::eventRangeColumnName;
202 module.attr("eventRangeColumnName") = &columnar::eventRangeColumnName;
205 module.attr("invalid_link_value") = columnar::ColumnarModeArray::invalidLinkValue;
208 [](const std::string& data) { return CxxUtils::crc64(data); },
210 "CRC-64 of a string, using the Athena default polynomial "
211 "(CxxUtils::crc64).");
213 module.def("crc64addint", &CxxUtils::crc64addint,
215 "Extend a previously-calculated CRC-64 to include an integer "
216 "(CxxUtils::crc64addint).");
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).");
226#ifdef XAOD_STANDALONE
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.");
233 module.def("set_python_printer", &clear_printer,
234 "Reset to the default stdout message printer.");
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.");
246 nb::enum_<columnar::ColumnAccessMode>(module,
"ColumnAccessMode")
253 return "<ColumnAccessMode input>";
255 return "<ColumnAccessMode output>";
257 return "<ColumnAccessMode update>";
259 return "<ColumnAccessMode update value=" + std::to_string(
static_cast<int>(mode)) +
">";
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)
274 nb::class_<columnar::ColumnInfo>(module,
"ColumnInfo")
293 std::string access_mode;
296 access_mode =
"input";
299 access_mode =
"output";
302 access_mode =
"update";
306 access_mode =
"unknown";
308 return "<ColumnInfo name='" + self.
name +
"'" +
310 ", access_mode='" + access_mode +
"'" +
317 d[
"name"] = self.
name;
320 d[
"access_mode"] =
static_cast<int>(self.
accessMode);
334 nb::class_<columnar::PythonToolHandle>(module,
"PythonToolHandle")
342 std::cerr <<
"Warning: PythonToolHandle.type is empty."
343 <<
" Set with PythonToolHandle.set_type_and_name." << std::endl;
352 std::cerr <<
"Warning: PythonToolHandle.name is empty."
353 <<
" Set with PythonToolHandle.set_type_and_name." << std::endl;
359 .def(
"set_type_and_name",
364 "Set the type and name of the tool.")
368 "Set a property on the tool.")
372 "Set a property on the tool.")
374 .def(
"preinitialize",
376 "Preinitialize the tool.")
379 .def(
"rename_containers",
382 "Rename the columns the tool uses.")
385 .def(
"rename_containers",
387 std::vector<std::pair<std::string, std::string>> vectorized;
388 for (
const auto&
pair : renames)
389 vectorized.emplace_back(
pair);
394 "Rename the columns the tool uses.")
398 "Initialize the tool.")
400 .def(
"apply_systematic_variation",
405 "Apply a systematic variation to the tool.")
412 "Set a float column pointer.")
419 "Set a char column pointer.")
426 "Set an int column pointer.")
433 "Set a uint8_t column pointer.")
440 "Set a uint16_t column pointer.")
447 "Set a uint32_t column pointer.")
454 "Set a uint64_t column pointer.")
458 "key"_a,
"column"_a,
"is_const"_a =
true,
459 "Set a void column pointer (nanobind version).")
463 "Set a void immutable column pointer (nanobind version).")
467 "Get a column as a numpy array (zero-copy view into the tool's buffer).")
471 "Return the column names (enables dict(handle)).")
475 "Call the tool and reset the columns.")
480 "Get the expected column information."
483 .def(
"get_recommended_systematics",
485 "Get the recommended systematics.")
Definition of message levels and a helper function.
NB_MODULE(python_tool_handle, module)
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)
size_t size() const
Number of registered mappings.
void print(char *figname, TCanvas *c1)
A crc-64 implementation, using pclmul where possible.
constexpr unsigned columnarAccessMode
ColumnAccessMode
an enum for the different access modes for a column
@ update
an updateable column
a struct that contains meta-information about each column that's needed to interface the column with ...
std::string offsetName
the name of the offset column used for this column (or empty string for none)
std::string soleLinkTargetName
for simple link columns: the name of the target container
std::string keyColumnForVariantLink
if this is a key column for a variant link, the name of the associated link column
std::uint32_t soleLinkTargetClid
for simple link columns: the CLID of the target container
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
bool isOptional
whether this column is optional
std::vector< std::string > variantLinkTargetNames
for variant link key columns: the names of the containers we can link to
std::string name
the name of the column
bool isOffset
whether this is an offset column
ColumnAccessMode accessMode
the access mode for the column
std::string replacesColumn
whether this replaces another column
bool isVariantLink
whether this is a variant link column
unsigned index
the index of the column in the data array
const std::type_info * type
the type of the individual entries in the column