HOPS
HOPS class reference
MHO_PyTableContainer.hh
Go to the documentation of this file.
1 #ifndef MHO_PyTableContainer_HH__
2 #define MHO_PyTableContainer_HH__
3 
4 #include <array>
5 #include <complex>
6 #include <sstream>
7 #include <string>
8 #include <tuple>
9 #include <type_traits>
10 
11 #include "MHO_ExtensibleElement.hh"
12 #include "MHO_Meta.hh"
13 #include "MHO_NDArrayWrapper.hh"
14 #include "MHO_TableContainer.hh"
16 
17 #include <pybind11/numpy.h> //this is important to have for std::complex<T> support!
18 #include <pybind11/pybind11.h>
19 #include <pybind11/stl.h>
20 namespace py = pybind11;
21 
22 // #include "MHO_PyAxisHelpers.hh"
23 
24 namespace hops
25 {
26 
39 template< typename XTableType > class MHO_PyTableContainer
40 {
41  public:
42  MHO_PyTableContainer(MHO_ExtensibleElement* element): fElement(element)
43  {
44  fRank = XTableType::rank::value;
45  fTable = dynamic_cast< XTableType* >(element);
46  };
47 
48  virtual ~MHO_PyTableContainer(){};
49 
50  std::size_t GetRank() const { return fTable->GetRank(); }
51 
52  std::size_t GetDimension(std::size_t index) const { return fTable->GetDimension(index); }
53 
54  std::string GetClassName() const { return MHO_ClassName< XTableType >(); }
55 
56  /*!*return the ND-array data block as a numpy array
57  *this transfer is copy-free
58  */
59  py::array_t< typename XTableType::value_type > GetNumpyArray()
60  {
61  auto strides = fTable->GetStrideArray();
62  for(std::size_t i = 0; i < fRank; i++)
63  {
64  strides[i] *= sizeof(typename XTableType::value_type);
65  }
66  py::array_t< typename XTableType::value_type > ret_val{
67  fTable->GetDimensionArray(), strides, fTable->GetData(),
68  py::str(fDummy) //dummy owner, to keep python from taking ownership of this memory
69  };
70  return ret_val;
71  }
72 
73  /*!* return the N-th axis as a python list object
74  *this conversion is NOT copy-free, but we get the same return type for all axis types
75  */
76  py::list GetCoordinateAxis(size_t index)
77  {
78  py::list ret_val;
79  if(index < fRank)
80  {
81  PyListFiller filler(&ret_val);
82  apply_at< typename XTableType::axis_pack_tuple_type, PyListFiller >(*fTable, index, filler);
83  }
84  else
85  {
86  msg_error("python_bindings", "axis index: " << index << " exceeds table rank of: " << fRank << eom);
87  std::stringstream ss;
88  ss << "axis index: " << index << " exceeds table rank of: " << fRank;
89  py::print("error: ", ss.str());
90  }
91  return ret_val;
92  }
93 
94  //super crude way to modify the coordinate
95  void SetCoordinateLabel(std::size_t axis_index, std::size_t label_index, py::object label)
96  {
97  if(axis_index < fRank)
98  {
99  PyAxisLabelModifier modifier(label_index, &label);
100  apply_at< typename XTableType::axis_pack_tuple_type, PyAxisLabelModifier >(*fTable, axis_index, modifier);
101  }
102  else
103  {
104  msg_error("python_bindings", "axis index: " << axis_index << " exceeds table rank of: " << fRank << eom);
105  std::stringstream ss;
106  ss << "axis index: " << axis_index << " exceeds table rank of: " << fRank;
107  py::print("error: ", ss.str());
108  }
109  }
110 
111  py::dict GetMetaData() { return GetTableTags< XTableType >(fTable); }
112 
113  void SetMetaData(py::dict metadata) { return SetTableTags< XTableType >(fTable, metadata); }
114 
115  py::dict GetCoordinateAxisMetaData(std::size_t index)
116  {
117  py::dict ret_val;
118  if(index < fRank)
119  {
120  PyAxisMetaDataFiller filler(&ret_val);
121  apply_at< typename XTableType::axis_pack_tuple_type, PyAxisMetaDataFiller >(*fTable, index, filler);
122  }
123  else
124  {
125  msg_error("python_bindings", "axis index: " << index << " exceeds table rank of: " << fRank << eom);
126  std::stringstream ss;
127  ss << "axis index: " << index << " exceeds table rank of: " << fRank;
128  py::print("error: ", ss.str());
129  }
130  return ret_val;
131  }
132 
133  //whole-sale re-setting of metadata, this may be over-kill and actually a bit dangerous
134  //since the index/interval labels are stored in the metadata object, if we are not
135  //passing an object which is derived from an already retrieved copy we may lose info
136  void SetCoordinateAxisMetaData(std::size_t index, py::dict metadata)
137  {
138  if(index < fRank)
139  {
140  PyAxisMetaDataSetter filler(&metadata);
141  apply_at< typename XTableType::axis_pack_tuple_type, PyAxisMetaDataSetter >(*fTable, index, filler);
142  }
143  else
144  {
145  msg_error("python_bindings", "axis index: " << index << " exceeds table rank of: " << fRank << eom);
146  std::stringstream ss;
147  ss << "axis index: " << index << " exceeds table rank of: " << fRank;
148  py::print("error: ", ss.str());
149  }
150  }
151 
152  private:
153  template< typename XDataTableType > static py::dict GetTableTags(XDataTableType* table)
154  {
155  py::dict tags = table->GetMetaDataAsJSON();
156  return tags;
157  }
158 
159  template< typename XDataTableType > static void SetTableTags(XDataTableType* table, py::dict metadata)
160  {
161  mho_json md = metadata;
162  table->SetMetaDataAsJSON(md);
163  }
164 
165  //helper class to act as a python-list filling functor (to return copies)
166  class PyListFiller
167  {
168  public:
169  PyListFiller(py::list* alist): fList(alist){};
170  ~PyListFiller(){};
171 
172  template< typename XAxisType > void operator()(const XAxisType& axis)
173  {
174  for(size_t i = 0; i < axis.GetSize(); i++)
175  {
176  fList->append(axis[i]);
177  }
178  }
179 
180  private:
181  py::list* fList;
182  };
183 
184  //helper class to act as a python dict filling functor (to return copies of meta data)
185  class PyAxisMetaDataFiller
186  {
187  public:
188  PyAxisMetaDataFiller(py::dict* adict): fDict(adict){};
189  ~PyAxisMetaDataFiller(){};
190 
191  template< typename XAxisType > void operator()(const XAxisType& axis) { *fDict = axis.GetMetaDataAsJSON(); }
192 
193  private:
194  py::dict* fDict;
195  };
196 
197  //helper class to act as a python dict filling functor (to return copies of meta data)
198  class PyAxisMetaDataSetter
199  {
200  public:
201  PyAxisMetaDataSetter(py::dict* adict): fDict(adict){};
202  ~PyAxisMetaDataSetter(){};
203 
204  template< typename XAxisType > void operator()(XAxisType& axis) { axis.SetMetaDataAsJSON(*fDict); }
205 
206  private:
207  py::dict* fDict;
208  };
209 
210  //helper class that allows us to set the value of an axis label from python
211  class PyAxisLabelModifier
212  {
213  public:
214  //constructor accepts an coordinate index and a python object
215  //and will attempt to case the object to the underyling axis-label type
216  //and assign it at the location specified by the index
217  PyAxisLabelModifier(std::size_t index, py::object* label_object): fIndex(index), fObject(label_object){};
218  ~PyAxisLabelModifier(){};
219 
220  template< typename XAxisType > void operator()(XAxisType& axis)
221  {
222  typename XAxisType::value_type label_value;
223  label_value = fObject->cast< typename XAxisType::value_type >();
224 
225  //expect to get some sort of MHO_Axis
226  if(fIndex < axis.GetSize())
227  {
228  axis(fIndex) = label_value;
229  }
230  else
231  {
232  msg_error("python_bindings", "error axis coordinate index out of bounds: "
233  << fIndex << " > " << axis.GetSize() << "." << eom);
234  std::stringstream ss;
235  ss << "axis coordinate index out of bounds: " << fIndex << " > " << axis.GetSize();
236  py::print("error: ", ss.str());
237  }
238  }
239 
240  private:
241  std::size_t fIndex;
242  py::object* fObject;
243  };
244 
245  private:
246  MHO_ExtensibleElement* fElement;
247  XTableType* fTable;
248  unsigned int fRank;
249  std::string fDummy;
250 };
251 
252 //XTableType must inherit from MHO_NDArrayWrapper<XValueType, RANK>
253 template< typename XTableType > void DeclarePyTableContainer(py::module& m, std::string pyclass_name = "")
254 {
255  if(pyclass_name == "")
256  {
257  pyclass_name = MHO_ClassName< XTableType >();
258  }
259 
260  py::class_< MHO_PyTableContainer< XTableType > >(m, pyclass_name.c_str())
261  //no __init__ def here, as this class is not meant to be constructable on the python side
263  "get the rank (number of dimensions) of the table object")
264  .def("get_classname", &hops::MHO_PyTableContainer< XTableType >::GetClassName, "get the name of the table class")
266  "get the size of the associated table dimension", py::arg("dimension_index"))
267  .def("get_metadata", &hops::MHO_PyTableContainer< XTableType >::GetMetaData, py::return_value_policy::copy,
268  "get a copy of the meta data attached to this table as a dictionary")
270  "replace the meta data attached to this table with the passed dictionary", py::arg("metadata_object"))
271  .def("get_numpy_array", &hops::MHO_PyTableContainer< XTableType >::GetNumpyArray, py::return_value_policy::reference,
272  "get the underyling table data as a multidimensional numpy array")
273  .def("get_axis", &hops::MHO_PyTableContainer< XTableType >::GetCoordinateAxis, py::return_value_policy::reference,
274  "get the axis container associated with the dimension index specified", py::arg("index"))
276  py::return_value_policy::copy,
277  "get a copy of the meta data attached to axis associated with the dimension index specified", py::arg("index"))
279  "replace the meta data attached to the axis associated with the dimension index specified, with passed dictionary "
280  "metadata_object",
281  py::arg("index"), py::arg("metadata_object"))
283  "modify the axis (specified by the dimension_index) at the coordinate location specified by coordinate_index, "
284  "replacing the existing value with label_value",
285  py::arg("dimension_index"), py::arg("coordinate_index"), py::arg("label_value"));
286 }
287 
288 } // namespace hops
289 
290 #endif
nlohmann::json mho_json
Definition: MHO_JSONHeaderWrapper.hh:5
#define msg_error(xKEY, xCONTENT)
Definition: MHO_Message.hh:244
template meta-programming helper functions, mostly tuple access/modification
int axis(char *y_axis, char *x_axis)
Definition: axis.c:22
Class MHO_ExtensibleElement.
Definition: MHO_ExtensibleElement.hh:60
python bindings for template MHO_TableContainer objects This extension which allows us to present the...
Definition: MHO_PyTableContainer.hh:40
MHO_PyTableContainer(MHO_ExtensibleElement *element)
Definition: MHO_PyTableContainer.hh:42
void SetCoordinateLabel(std::size_t axis_index, std::size_t label_index, py::object label)
Definition: MHO_PyTableContainer.hh:95
void SetMetaData(py::dict metadata)
Definition: MHO_PyTableContainer.hh:113
std::size_t GetDimension(std::size_t index) const
Definition: MHO_PyTableContainer.hh:52
py::list GetCoordinateAxis(size_t index)
Definition: MHO_PyTableContainer.hh:76
py::array_t< typename XTableType::value_type > GetNumpyArray()
Definition: MHO_PyTableContainer.hh:59
py::dict GetMetaData()
Definition: MHO_PyTableContainer.hh:111
void SetCoordinateAxisMetaData(std::size_t index, py::dict metadata)
Definition: MHO_PyTableContainer.hh:136
std::size_t GetRank() const
Definition: MHO_PyTableContainer.hh:50
py::dict GetCoordinateAxisMetaData(std::size_t index)
Definition: MHO_PyTableContainer.hh:115
virtual ~MHO_PyTableContainer()
Definition: MHO_PyTableContainer.hh:48
std::string GetClassName() const
Definition: MHO_PyTableContainer.hh:54
def alist(filename)
Definition: afio.py:372
Definition: MHO_ChannelLabeler.hh:17
void DeclarePyTableContainer(py::module &m, std::string pyclass_name="")
Definition: MHO_PyTableContainer.hh:253
Definition: vex.h:94