Nanosecond times in Python (#522)

* initital implementation

* datetime replaces with sls::Duration in Python C bindings

* using custom type caster

* fix for conversion to seconds

* added set_count in python

* common header for pybind11 includes

authored-by: Erik Frojdh <erik.frojdh@psi.ch>
This commit is contained in:
Erik Fröjdh
2022-08-26 11:48:40 +02:00
committed by GitHub
parent 3970ed2560
commit 045a28b5de
21 changed files with 2081 additions and 1800 deletions

View File

@ -0,0 +1,26 @@
#include "DurationWrapper.h"
#include <cmath>
namespace sls{
DurationWrapper::DurationWrapper(double seconds){
ns_tick = std::round(seconds*1e9);
}
uint64_t DurationWrapper::count() const{
return ns_tick;
}
void DurationWrapper::set_count(uint64_t ns_count){
ns_tick = ns_count;
}
bool DurationWrapper::operator==(const DurationWrapper& other)const{
return ns_tick == other.ns_tick;
}
double DurationWrapper::total_seconds()const{
return static_cast<double>(ns_tick)/1e9;
}
}

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include <cstdint>
namespace sls{
/*
Wrapper for nanoseconds stored in uint64_t, used for conversion between
std::chrono::nanoseconds and python (float or sls::DurationWrapper)
*/
class DurationWrapper{
uint64_t ns_tick{0};
public:
DurationWrapper() = default;
explicit DurationWrapper(double seconds);
~DurationWrapper() = default;
bool operator==(const DurationWrapper& other) const;
uint64_t count() const;
void set_count(uint64_t count);
double total_seconds() const;
};
}

View File

@ -1,12 +1,8 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
// #include "sls/Pattern.h"
#include "py_headers.h"
#include "sls/ToString.h"
#include "sls/sls_detector_defs.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "sls/Detector.h"
#include "sls/ToString.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "typecaster.h"
#include "sls/TimeHelper.h"
#include <array>
#include <chrono>
namespace py = pybind11;
@ -23,7 +19,7 @@ void init_det(py::module &m) {
using sls::Result;
py::class_<Detector> CppDetectorApi(m, "CppDetectorApi");
CppDetectorApi.def(py::init<int>())
CppDetectorApi.def(py::init<int>());
[[FUNCTIONS]]
}

21
python/src/duration.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "py_headers.h"
#include "DurationWrapper.h"
#include <sstream>
namespace py = pybind11;
using sls::DurationWrapper;
void init_duration(py::module &m) {
py::class_<DurationWrapper>(m, "DurationWrapper")
.def(py::init())
.def(py::init<double>())
.def("total_seconds", &DurationWrapper::total_seconds)
.def("count", &DurationWrapper::count)
.def("set_count", &DurationWrapper::set_count)
.def("__repr__", [](const DurationWrapper &self) {
std::stringstream ss;
ss << "sls::DurationWrapper(total_seconds: " << self.total_seconds()
<< " count: " << self.count() << ")";
return ss.str();
});
}

View File

@ -3,11 +3,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "sls/Pattern.h"
#include "sls/sls_detector_defs.h"

View File

@ -1,10 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "sls/Pattern.h"
#include "sls/sls_detector_defs.h"

View File

@ -1,9 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "mythenFileIO.h"
#include "sls/Detector.h"
@ -11,8 +8,6 @@
#include <chrono>
#include <vector>
#include "typecaster.h"
using ds = std::chrono::duration<double>;
namespace py = pybind11;
@ -23,6 +18,7 @@ void init_network(py::module &);
void init_pattern(py::module &);
void init_scan(py::module &);
void init_source(py::module &);
void init_duration(py::module &);
PYBIND11_MODULE(_slsdet, m) {
m.doc() = R"pbdoc(
C/C++ API
@ -40,6 +36,7 @@ PYBIND11_MODULE(_slsdet, m) {
init_pattern(m);
init_scan(m);
init_source(m);
init_duration(m);
// init_experimental(m);
py::module io = m.def_submodule("io", "Submodule for io");

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include "py_headers.h"
#include <cassert>
#include <cmath>
@ -9,9 +10,6 @@
#include <iostream>
#include <vector>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
template <size_t bit_index0, size_t bit_index1>

View File

@ -4,11 +4,7 @@
This file contains Python bindings for the IpAddr and MacAddr
classes.
*/
#include <pybind11/chrono.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "sls/network_utils.h"
namespace py = pybind11;

View File

@ -1,10 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include <pybind11/chrono.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "py_headers.h"
#include "sls/Pattern.h"
#include "sls/sls_detector_defs.h"

14
python/src/py_headers.h Normal file
View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
/*
Single common header file to make sure the pybind includes are the
same and ordered in the same way in all files. Needed to avoid
ODR warnings
*/
#pragma once
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include "typecaster.h"

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "py_headers.h"
#include "sls/sls_detector_defs.h"
#include <pybind11/chrono.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <sstream>
namespace py = pybind11;
void init_scan(py::module &m) {

View File

@ -1,13 +1,92 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include "sls/Result.h"
#include <pybind11/pybind11.h>
// Add type_typecaster to pybind for our wrapper type
#include <datetime.h>
#include "sls/Result.h"
#include "DurationWrapper.h"
namespace py = pybind11;
namespace pybind11 {
namespace detail {
template <typename Type, typename Alloc>
struct type_caster<sls::Result<Type, Alloc>>
: list_caster<sls::Result<Type, Alloc>, Type> {};
// Based on the typecaster in pybind11/chrono.h
template <> struct type_caster<std::chrono::nanoseconds> {
public:
PYBIND11_TYPE_CASTER(std::chrono::nanoseconds, const_name("DurationWrapper"));
// signed 25 bits required by the standard.
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
/**
* Conversion part 1 (Python->C++): convert a PyObject into std::chrono::nanoseconds
* try datetime.timedelta, floats and our DurationWrapper wrapper
*/
bool load(handle src, bool) {
using namespace std::chrono;
// Lazy initialise the PyDateTime import
if (!PyDateTimeAPI) {
PyDateTime_IMPORT;
}
if (!src) {
return false;
}
// If invoked with datetime.delta object, same as in chrono.h
if (PyDelta_Check(src.ptr())) {
value = duration_cast<nanoseconds>(
days(PyDateTime_DELTA_GET_DAYS(src.ptr())) +
seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) +
microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))
);
return true;
}
// If invoked with a float we assume it is seconds and convert, same as in chrono.h
if (PyFloat_Check(src.ptr())) {
value = duration_cast<nanoseconds>(duration<double>(PyFloat_AsDouble(src.ptr())));
return true;
}
// Lastly if we were actually called with a DurationWrapper object we get
// the number of nanoseconds and create a std::chrono::nanoseconds from it
py::object py_cls = py::module::import("_slsdet").attr("DurationWrapper");
if (py::isinstance(src, py_cls)){
sls::DurationWrapper *cls = src.cast<sls::DurationWrapper *>();
value = nanoseconds(cls->count());
return true;
}
return false;
}
/**
* Conversion part 2 (C++ -> Python)
* import the module to get a handle to the wrapped class
* Default construct an object of (wrapped) DurationWrapper
* set the count from chrono::nanoseconds and return
*/
static handle cast(std::chrono::nanoseconds src, return_value_policy /* policy */, handle /* parent */) {
py::object py_cls = py::module::import("_slsdet").attr("DurationWrapper");
py::object* obj = new py::object;
*obj = py_cls();
sls::DurationWrapper *dur = obj->cast<sls::DurationWrapper *>();
dur->set_count(src.count());
return *obj;
}
};
} // namespace detail
} // namespace pybind11