psana_python/src/python_converter.cpp

Go to the documentation of this file.
00001 #include <psana_python/python_converter.h>
00002 
00003 // Check APIs depricated in current version (NUMPY 1.7) are used
00004 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
00005 #define PY_ARRAY_UNIQUE_SYMBOL PSALG_NUMPY_NDARRAY_CONVERTER
00006 #include <numpy/arrayobject.h>
00007 
00008 #include <boost/preprocessor/seq/for_each_product.hpp>
00009 #include <boost/preprocessor/seq/elem.hpp>
00010 #include <boost/preprocessor/seq/for_each.hpp>
00011 
00012 #include <pytools/PyDataType.h>
00013 
00014 #include <PSEvt/Event.h>
00015 #include <psana_python/Event.h>
00016 
00017 #include <PSEnv/Env.h>
00018 #include <psana_python/Env.h>
00019 
00020 #include <PSEvt/Source.h>
00021 #include <psana_python/Source.h>
00022 
00023 
00024 #include <string>
00025 #include <iostream>
00026 #include "MsgLogger/MsgLogger.h"
00027 
00028 
00029 // set of ranks and types for which we instantiate converters
00030 #define ND_RANKS (1)(2)(3)(4)(5)(6)
00031 #define ND_TYPES (int8_t)(uint8_t)(int16_t)(uint16_t)(int32_t)(uint32_t)(int64_t)(uint64_t)(float)(double)
00032 #define CONST_ND_TYPES (const int8_t)(const uint8_t)(const int16_t)(const uint16_t)(const int32_t)(const uint32_t)(const int64_t)(const uint64_t)(const float)(const double)
00033  
00034 
00035 namespace psana_python {
00036 
00037   // String to identify debug statements produced by this code
00038   const char *pyConverterlogger = "Python_Converter";
00039   
00040   void createConverters() {
00041     
00042     // Initialise NUMPY 
00043     import_array();
00044 
00045     // These templates and BOOST preprocessor macros can be hard to understand what's
00046     // going on.  Here's a couple of examples of what they trying to do....
00047     //
00048     // Let's say we want converters for doubles NDarrays for 1-3 dimensions   
00049     // Without the BOOST macros above we would have to do this:
00050     // 
00051     // boost::python::to_python_converter<ndarray<double,1>,NDArrayToNumpy<double,1> >();
00052     // boost::python::to_python_converter<ndarray<double,2>,NDArrayToNumpy<double,2> >();
00053     // boost::python::to_python_converter<ndarray<double,3>,NDArrayToNumpy<double,3> >();
00054     //  
00055     // NumpyToNDArray<float,1>().from_python();
00056     // NumpyToNDArray<float,2>().from_python();
00057     // NumpyToNDArray<float,3>().from_python();
00058     // 
00059     // Rather than typing each out each coverter, we used the BOOST
00060     // preprosser macros to generate the code for us.
00061     
00062     
00063     // Preprocessor macro to define the call to create a NDArray to NUMPY converter. 
00064     #define REGISTER_NDARRAY_TO_NUMPY_CONVERTER(r,PRODUCT)                      \
00065       psana_python::NDArrayToNumpy<BOOST_PP_SEQ_ELEM(0,PRODUCT),BOOST_PP_SEQ_ELEM(1,PRODUCT)>().register_ndarray_to_numpy_cvt();
00066 
00067 
00068     // Preprocessor macro to define the call to create a NUMPY to NDArray converter 
00069     #define REGISTER_NUMPY_TO_NDARRAY_CONVERTER(r,PRODUCT)                      \
00070       psana_python::NumpyToNDArray<BOOST_PP_SEQ_ELEM(0,PRODUCT),BOOST_PP_SEQ_ELEM(1,PRODUCT)>().from_python();
00071   
00072 
00073     // BOOST preprocessor macro to create converters 1-6 dimensional NDArray, with
00074     // data type from int to double, both const and non const  
00075     BOOST_PP_SEQ_FOR_EACH_PRODUCT(REGISTER_NDARRAY_TO_NUMPY_CONVERTER,(ND_TYPES)(ND_RANKS))
00076     BOOST_PP_SEQ_FOR_EACH_PRODUCT(REGISTER_NDARRAY_TO_NUMPY_CONVERTER,(CONST_ND_TYPES)(ND_RANKS))
00077       
00078     BOOST_PP_SEQ_FOR_EACH_PRODUCT(REGISTER_NUMPY_TO_NDARRAY_CONVERTER,(ND_TYPES)(ND_RANKS))
00079     BOOST_PP_SEQ_FOR_EACH_PRODUCT(REGISTER_NUMPY_TO_NDARRAY_CONVERTER,(CONST_ND_TYPES)(ND_RANKS))
00080 
00081 
00082     // Register the STL list<ndtypes> to Numpy converters with BOOST
00083     #define REGISTER_STLLIST_TO_NUMPY_CONVERTER(r,data, ELEMENT)                \
00084       psana_python::StlListToNumpy< ELEMENT >().register_stllist_to_numpy_cvt();
00085 
00086     BOOST_PP_SEQ_FOR_EACH(REGISTER_STLLIST_TO_NUMPY_CONVERTER,BOOST_PP_EMPTY(),ND_TYPES)
00087 
00088     // Register Python-Event to Event converter  
00089     psana_python::PyEvtToEvt().from_python();
00090     
00091     // Register Python-Env to Env converter  
00092     psana_python::PyEnvToEnv().from_python();
00093 
00094     // Register Python-Source to Source converter  
00095     psana_python::PySourceToSource().from_python();
00096     
00097     return;
00098   }
00099 
00100 
00101   namespace {
00102 
00103     // type traits for selected set of C++ types that we support as
00104     // elements of ndarrays
00105     template <typename T> struct Traits {};
00106   
00107     // ===> SHOULD BE ABLE TO REPLACE THIS WITH BOOST MACROS 
00108   
00109     // non-const data types
00110     template <> struct Traits<int8_t> {
00111       static const char* typeName() { return "int8"; }
00112       static int numpyType() { return NPY_INT8; }
00113     };
00114     template <> struct Traits<uint8_t> {
00115       static const char* typeName() { return "uint8"; }
00116       static int numpyType() { return NPY_UINT8; }
00117     };
00118     template <> struct Traits<int16_t> {
00119       static const char* typeName() { return "int16"; }
00120       static int numpyType() { return NPY_INT16; }
00121     };
00122     template <> struct Traits<uint16_t> {
00123       static const char* typeName() { return "uint16"; }
00124       static int numpyType() { return NPY_UINT16; }
00125     };
00126     template <> struct Traits<int32_t> {
00127       static const char* typeName() { return "int32"; }
00128       static int numpyType() { return NPY_INT32; }
00129     };
00130     template <> struct Traits<uint32_t> {
00131       static const char* typeName() { return "uint32"; }
00132       static int numpyType() { return NPY_UINT32; }
00133     };
00134     template <> struct Traits<int64_t> {
00135       static const char* typeName() { return "int64"; }
00136       static int numpyType() { return NPY_INT64; }
00137     };
00138     template <> struct Traits<uint64_t> {
00139     static const char* typeName() { return "uint64"; }
00140       static int numpyType() { return NPY_UINT64; }
00141     };
00142     template <> struct Traits<float> {
00143       static const char* typeName() { return "float32"; }
00144       static int numpyType() { return NPY_FLOAT32; }
00145     };
00146     template <> struct Traits<double> {
00147       static const char* typeName() { return "float64"; }
00148       static int numpyType() { return NPY_FLOAT64; }
00149     };
00150     
00151     
00152     // const data types
00153     template <> struct Traits<const int8_t> {
00154       static const char* typeName() { return "int8"; }
00155       static int numpyType() { return NPY_INT8; }
00156     };
00157     template <> struct Traits<const uint8_t> {
00158       static const char* typeName() { return "uint8"; }
00159       static int numpyType() { return NPY_UINT8; }
00160     };
00161     template <> struct Traits<const int16_t> {
00162       static const char* typeName() { return "int16"; }
00163       static int numpyType() { return NPY_INT16; }
00164     };
00165     template <> struct Traits<const uint16_t> {
00166       static const char* typeName() { return "uint16"; }
00167       static int numpyType() { return NPY_UINT16; }
00168     };
00169     template <> struct Traits<const int32_t> {
00170       static const char* typeName() { return "int32"; }
00171       static int numpyType() { return NPY_INT32; }
00172     };
00173     template <> struct Traits<const uint32_t> {
00174       static const char* typeName() { return "uint32"; }
00175       static int numpyType() { return NPY_UINT32; }
00176     };
00177     template <> struct Traits<const int64_t> {
00178       static const char* typeName() { return "int64"; }
00179       static int numpyType() { return NPY_INT64; }
00180     };
00181     template <> struct Traits<const uint64_t> {
00182       static const char* typeName() { return "uint64"; }
00183       static int numpyType() { return NPY_UINT64; }
00184     };
00185     template <> struct Traits<const float> {
00186       static const char* typeName() { return "float32"; }
00187       static int numpyType() { return NPY_FLOAT32; }
00188     };
00189     template <> struct Traits<const double> {
00190       static const char* typeName() { return "float64"; }
00191       static int numpyType() { return NPY_FLOAT64; }
00192     };
00193     
00194 
00195     // Returns true if strides correspond to C memory layout
00196     template <unsigned Rank>
00197     bool isCArray(const unsigned shape[], const int strides[]) {
00198       int stride = 1;
00199       for (int i = Rank; i > 0; -- i) {
00200         if (strides[i-1] != stride) return false;
00201         stride *= shape[i-1];
00202       }
00203       return true;
00204     }
00205     
00206   }
00207   
00208   
00209     
00210   // ***************************************************************************
00211   // ***************************************************************************
00212   //    NDARRAY TO NUMPY CONVERTER 
00213   // ***************************************************************************
00214   // ***************************************************************************
00215   
00216   namespace {
00217     // Special Python wrapper object for ndarray
00218     template <typename T, unsigned Rank>
00219     class NDarrayWrapper : 
00220       public pytools::PyDataType<NDarrayWrapper<T, Rank>, ndarray<T, Rank> > {  };
00221   }
00222 
00223   // NDArray to Numpy converter for BOOST
00224   template <typename T, unsigned Rank>
00225   PyObject* NDArrayToNumpy<T,Rank>::convert(ndarray<T, Rank> const& array) 
00226   {
00227     MsgLog(pyConverterlogger, debug, "Calling converter from ndarray<"
00228            << Traits<T>::typeName() << "," << Rank << "> to python object"); 
00229         
00230     // item size
00231     const size_t itemsize = sizeof(T);
00232     
00233     // Convert itemsize to numpy type number
00234     // For now, use templated function from NdarrayCvt.h
00235     const int typenum = Traits<T>::numpyType();
00236     
00237     // Dimensions and strides 
00238     npy_intp dims[Rank], strides[Rank];
00239     
00240     // Copy dim and strides from ndarry to numpy
00241     // NB: Numpy strides are in bytes    
00242     for (unsigned i=0; i<Rank; i++) {
00243       dims[i] = array.shape()[i];
00244       strides[i] = array.strides()[i] * itemsize;
00245     }
00246 
00247     // Grab underlying ndarry data pointer.
00248     // Have to cast to void* for numpy creation
00249     // The const is needed incase we instatiate a const type ndarray
00250     // 
00251     // NB: IT IS BELIEVED THAT THIS WILL INCREMENT NDARRAY'S INTERNAL SMART
00252     // POINTER REFERENCE COUNT. SO THIS SHOULD BE MEMEORY SAFE.
00253     // BUT ONLY IF NDARRAY IS CREATED USING METHOD 2 OR 3 AS DOCUMENTED IN NDARRAY
00254     // MAN PAGES.
00255     // IF NDARRAY IS CREATED USING METHOD 1 (SEE NDARRAY WEB DOCS), THE USER IS RESPONSIBLE
00256     // FOR MEMORY MANAGEMENT. THUS THE ARRAY DATA COULD GET DELETED WITHOUT INFORMING PYTHON
00257     // 
00258     const void* data = array.data();
00259 
00260 
00261     // Set the numpy flags 
00262     int flags = 0;   //==> initialise to zero
00263 
00264     // Start by setting outgoing numpy array is writable
00265     flags |= NPY_ARRAY_WRITEABLE;  
00266 
00267     // Check NDArray is C array 
00268     // For now, use function from NdarrayCvt.h
00269     if (psana_python::isCArray<Rank>(array.shape(),array.strides())) {
00270       flags |= NPY_ARRAY_C_CONTIGUOUS;          
00271     }
00272 
00273     // Check NDArray is aligned
00274     if (reinterpret_cast<size_t>(array.data()) % itemsize == 0) {
00275       flags |= NPY_ARRAY_ALIGNED;
00276     }
00277     
00278     // Create the outgoing numpy array
00279     // Note the const_cast, as the PyArray_New only accepts non-const
00280     PyObject* outgoing_numpy_array = PyArray_New(&PyArray_Type,
00281                                                  Rank,
00282                                                  dims,
00283                                                  typenum,
00284                                                  strides,
00285                                                  const_cast<void*>(data),
00286                                                  itemsize,
00287                                                  flags,
00288                                                  0);
00289 
00290     // The next block of code needs some explanation....
00291     //
00292     // When constructed the outgoing numpy array, we pased the data
00293     // pointer, which is a pointer to the raw NDarray array data. The
00294     // alternative would be to copy all the data, which could be an
00295     // issue if the NDarry is huge (eg: pnCCD data, CSPad data).
00296     //
00297     // Given that we've passed the raw array pointer to numpy, we
00298     // need to tell numpy that it does not own the raw NDarray data
00299     // pointer. If we didn't do this, then when the outgoing numpy
00300     // array goes out of scope, python will delete the outgoing numpy
00301     // array, which in turn will delete the raw NDarray pointer
00302     // instead of calling NDArray's destructor.  When the NDArray
00303     // destructor gets call, it still believe it own the raw data
00304     // pointer, try to release it, and we'll have a crash (!).  
00305     // 
00306     // Numpy has a mechanism for dealing with data it does not
00307     // own. You set the base value to point to another python object
00308     // that does own So when the numpy array gets deleted, it will call
00309     // the owning python object and it will take care of the
00310     // memory. What we'll do here is create a PYTHON object that'll
00311     // call the NDArray destructor when it goes out of scope. 
00312 
00313     // POINT TO NOTE: There are three reference counts here: NUMPY,
00314     // PYTHON TRACKING OBJECT (See below), and BOOST reference count.
00315     // The line const void* data = array.data(); increments the BOOST
00316     // reference count ASSUMING that the original NDARRAY was created
00317     // using methods 2 or 3 (see NDARRAY web docs).  The PYTHON
00318     // TRACKING OBJECT reference count takes place in the line below
00319     // when the numpy_array_tracking_object is created, which wraps
00320     // the NDARRAY destructor, as mentioned above.
00321     // THE NUMPY reference count is the usual PYTHON reference count,
00322     // thus if there are multiple copies of the NUMPY array, its
00323     // reference count will be >0. 
00324 
00325     // Create the python object to keep track of outgoing numpy array
00326     PyObject* numpy_array_tracking_object =
00327       NDarrayWrapper<T,Rank>::PyObject_FromCpp(array);
00328     
00329     // Set the base pointer of outgoing_numpy_array to
00330     // numpy_array_tracking_object 
00331     // (have to do some casting as PyArray_SetBaseObject excepts a
00332     // PyArrayObject* 
00333     PyArrayObject* aoptr_outgoing_numpy_array =
00334       reinterpret_cast<PyArrayObject*>(outgoing_numpy_array);
00335     PyArray_SetBaseObject(aoptr_outgoing_numpy_array,
00336                           numpy_array_tracking_object);    
00337     
00338 
00339     // Now return the outgoing numpy array
00340     return outgoing_numpy_array;
00341   }
00342 
00343 
00344   // Function to register converter
00345   template <typename T, unsigned Rank>
00346   void NDArrayToNumpy<T,Rank>::register_ndarray_to_numpy_cvt()  
00347   {
00348     // Check if converter already registered for this type
00349     boost::python::type_info tinfo = boost::python::type_id<ndarray<T, Rank> >();
00350     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00351     
00352     if (reg == NULL) {
00353       MsgLog(pyConverterlogger, debug,
00354              "REGISTER NDARRAY<" << Traits<T>::typeName() << "," << Rank << ">"
00355              << "TO NUMPY CONVERTER");      
00356       boost::python::to_python_converter<ndarray<T,Rank>,NDArrayToNumpy<T,Rank> >();
00357 
00358     } else if ( (*reg).m_to_python == NULL) {
00359       MsgLog(pyConverterlogger, debug,
00360              "REGISTER NDARRAY<" << Traits<T>::typeName() << "," << Rank << ">"
00361              << "TO NUMPY CONVERTER");      
00362       boost::python::to_python_converter<ndarray<T,Rank>,NDArrayToNumpy<T,Rank> >();
00363 
00364     } else {
00365       MsgLog(pyConverterlogger, debug,
00366              "NDARRAY<" << Traits<T>::typeName() << "," << Rank << ">"
00367              << "TO NUMPY CONVERTER ALREADY REGISTERED");
00368     } 
00369     
00370     return;
00371   }
00372 
00373 
00374   // ***************************************************************************
00375   // ***************************************************************************
00376   //    END OF NDARRAY TO NUMPY CONVERTER
00377   // ***************************************************************************
00378   // ***************************************************************************
00379   
00380   
00381   
00382   
00383   
00384   
00385   // ***************************************************************************
00386   // ***************************************************************************
00387   //    START OF NUMPY TO NDARRAY CONVERTER
00388   // ***************************************************************************
00389   // ***************************************************************************
00390 
00391   // Numpy to NDArray convert for BOOST
00392   template <typename T, unsigned Rank>
00393   NumpyToNDArray<T,Rank>& NumpyToNDArray<T,Rank>::from_python()
00394   {
00395     // Check if converter was already registered
00396     boost::python::type_info tinfo = boost::python::type_id<ndarray<T, Rank> >();
00397     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00398 
00399     // For debugging, printing out the contents of the reg pointer will be useful. 
00400     MsgLog(pyConverterlogger, debug,"reg:" << reg);
00401     MsgLog(pyConverterlogger, debug,"reg.m_to_python:" << (*reg).m_to_python);
00402     MsgLog(pyConverterlogger, debug,"reg.m_class_object:" << (*reg).m_class_object);
00403     MsgLog(pyConverterlogger, debug,"reg.lvalue_chain:" << (*reg).lvalue_chain);
00404     MsgLog(pyConverterlogger, debug,"reg.rvalue_chain:" << (*reg).rvalue_chain);
00405         
00406     if (reg == NULL) {
00407       boost::python::converter::registry::push_back(&NumpyToNDArray::convertible,
00408                                                     &NumpyToNDArray::construct,
00409                                                     boost::python::type_id< ndarray<T, Rank>  >() );    
00410       MsgLog(pyConverterlogger, debug,
00411              "REGISTER BOOST PYTHON converter for NUMPY to NDARRAY"
00412              << "<" << Traits<T>::typeName() << "," << Rank << ">");  
00413 
00414     } else if ((*reg).rvalue_chain == NULL && (*reg).lvalue_chain == NULL) {
00415       boost::python::converter::registry::push_back(&NumpyToNDArray::convertible,
00416                                                     &NumpyToNDArray::construct,
00417                                                     boost::python::type_id< ndarray<T, Rank>  >() );
00418       MsgLog(pyConverterlogger, debug,
00419              "REGISTER BOOST PYTHON converter for NUMPY to NDARRAY"
00420              << "<" << Traits<T>::typeName() << "," << Rank << ">");  
00421       // NB:When Numpy-->NDArray converter is missing, both rvalue_chain and lvalue_chain are NULL
00422       // NB: the rvalue and lvalue was only checked emperically. So could be incorrect...
00423       
00424     } else {
00425       MsgLog(pyConverterlogger, debug,
00426              "BOOST PYTHON converter for NUMPY to NDARRAY"
00427              << "<" << Traits<T>::typeName() << "," << Rank << "> ALREADY REGISTERED");             
00428     } 
00429     
00430     return *this;
00431   }
00432 
00433 
00434   // Check object can be converted
00435   template <typename T, unsigned Rank> 
00436   void* NumpyToNDArray<T,Rank>::convertible(PyObject* obj) 
00437   {
00438     MsgLog(pyConverterlogger, debug,"CHECKING PYTHON OBJECT IS A NUMPY ARRAY");
00439     MsgLog(pyConverterlogger, debug,
00440            "Value from PyArray_Check " << PyArray_Check(obj) );
00441 
00442     if ( !PyArray_Check(obj) ) {
00443       MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS NOT A NUMPY ARRAY");
00444       return NULL;
00445      }
00446 
00447     PyArrayObject* arrayPtr = reinterpret_cast<PyArrayObject*>(obj);
00448     const int rank = PyArray_NDIM(arrayPtr);
00449     if (rank != Rank) {
00450       MsgLog(pyConverterlogger, debug,
00451              "INCORRECT NUMBER OF DIMENSIONS. Expected:" << Rank << " Got:" << rank);
00452       return NULL;
00453     }
00454 
00455     if (Traits<T>::numpyType() != PyArray_TYPE(arrayPtr)) {
00456       MsgLog(pyConverterlogger, debug,
00457              "INCORRECT TYPE.  Expected " << Traits<T>::typeName()
00458              << " Got:" << PyArray_TYPE(arrayPtr));
00459       return NULL;
00460     }
00461 
00462     MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS A NUMPY ARRAY");
00463     MsgLog(pyConverterlogger, debug,"Leaving convertible");
00464     return obj;
00465   }
00466 
00467 
00468 
00469 
00470 
00471   
00472   template <typename T, unsigned Rank>
00473   void NumpyToNDArray<T,Rank>::construct(PyObject* obj, BoostData* boostData) 
00474   {
00475     // Reminder that BoostData is a typedef defined in the header file
00476     //  --->  typedef boost::python::converter::rvalue_from_python_stage1_data BoostData;
00477     PyArrayObject* arrayPtr = reinterpret_cast<PyArrayObject*>(obj);
00478 
00479     const int itemsize = PyArray_ITEMSIZE(arrayPtr);
00480 
00481     // ==> Make a boost shared pointer to the underlying Numpy array 
00482     // ==> Do this for memory safety    
00483     T* array = reinterpret_cast<T*>(PyArray_DATA(arrayPtr));
00484 
00485     // ndarray<T,Rank>::shape_t shape[Rank]; // --> store array shape as C integers
00486     unsigned shape[Rank]; // --> store array shape as C integers
00487     int strides[Rank];     // --> store array strides as C integers
00488     for (unsigned i=0; i<Rank; i++) {
00489       shape[i] = PyArray_DIM(arrayPtr,i);
00490       // ==> NUMPY strides are in bytes
00491       strides[i] = PyArray_STRIDE(arrayPtr,i)/itemsize;
00492     }
00493 
00494     // Now that we have created out outgoing NDAraay, tell BOOST about
00495     // it, so it can pass it onto the calling C++ function
00496          
00497     // Get pointer to the converter's allocated memory block for the
00498     // outgoing NDArray
00499     // --> first create typedef storagetype for convenience
00500     typedef boost::python::converter::rvalue_from_python_storage<ndarray<T,Rank> > storagetype;
00501     // --> Now grab the pointer that BOOST has allocated to store the
00502     // --> NDArray
00503     void* storage = reinterpret_cast<storagetype*> (boostData)->storage.bytes;
00504 
00505     // --> Now set data's convertible attribute to outgoing NDArray
00506     MsgLog(pyConverterlogger, debug,"Creating outgoing ndarray");
00507     boostData->convertible = new(storage) ndarray<T,Rank>(array,shape);
00508 
00509     // NB: NDARRAY is created with a raw pointer.  That means NDARRAY
00510     // is not responsible for the memory, which is good in that the
00511     // calling C/C++ function cannot delete the data underneath NUMPY.
00512     // BUT if the NUMPY array goes out of scope in PYTHON, and the
00513     // calling C/C++ function still retains a reference to it, PYTHON
00514     // will delete the array data, and the C/C++ will be crash when
00515     // the C/C++ dereferences it.
00516 
00517     // Thus these converters should only be used for C/C++ functions 
00518     // that have no object persistance; ie:- PSALG functions are fine. 
00519     
00520     // For future thought.  If the NDArray constructor and destructor
00521     // could increment and decrement the PYTHON reference count, respectively,
00522     // via some intermediate object, this would allow robust memory-safe BOOST
00523     // converters with persistant objects. 
00524 
00525     
00526     // Set the strides of the outgoing NDArray
00527     ndarray<T,Rank>* outgoingArray = reinterpret_cast<ndarray<T,Rank>*>(boostData->convertible);
00528     outgoingArray->strides(strides);
00529 
00530     MsgLog(pyConverterlogger, debug,
00531            "Address of orignal Numpy data " << array);
00532     return;
00533   }
00534   // ***************************************************************************
00535   // ***************************************************************************
00536   //    END OF NDARRAY TO NUMPY CONVERTER
00537   // ***************************************************************************
00538   // ***************************************************************************
00539   
00540   
00541   
00542   
00543   
00544   
00545   // ***************************************************************************
00546   // ***************************************************************************
00547   //    START OF STL-LIST<NUMBERS> TO NUMPY CONVERTER
00548   // ***************************************************************************
00549   // ***************************************************************************
00550   
00551   // stl::list<ndtypes> to Numpy converter for BOOST
00552   // where ndtypes is defined by macro ND_TYPES defined at top of this header
00553   template <typename T>
00554   PyObject* StlListToNumpy<T>::convert( std::list<T> const& list ) 
00555   {
00556     MsgLog(pyConverterlogger, debug,
00557            "Calling converter from stl::list<" 
00558            <<  Traits<T>::typeName() << "> to python object");  
00559             
00560     // Convert itemsize to numpy type number    
00561     const int typenum = Traits<T>::numpyType();
00562     
00563     // Numpy array shape is set to be 1D and same size as list
00564     npy_intp dims[1];
00565     dims[0] = list.size();
00566             
00567     // Create a 1D numpy array that has same size as the list            
00568     PyObject* outgoing_numpy_array = PyArray_SimpleNew(1,dims,typenum);
00569     
00570     // Recast outgoing_numpy_array as PyArrayObject* to get access to data
00571     PyArrayObject* aoptr_outgoing_numpy_array =
00572       reinterpret_cast<PyArrayObject*>(outgoing_numpy_array);
00573       
00574     // Get pointer to underlying numpy array
00575     T* arrayData = reinterpret_cast<T*>(PyArray_DATA(aoptr_outgoing_numpy_array));
00576           
00577     // Deep copy contents of list to outgoing_numpy_array 
00578     // ...also avoids problems associated with lifetime of numpy and stl list
00579     typename std::list<T>::const_iterator listIter;
00580     for(listIter = list.begin(); listIter != list.end(); listIter++,arrayData++) 
00581       {
00582         MsgLog(pyConverterlogger, debug, *listIter);
00583         *arrayData = *listIter;
00584       }
00585 
00586     
00587     // Now return the outgoing numpy array
00588     return outgoing_numpy_array;
00589   }
00590   
00591 
00592 
00593   // Register STL list<number> to Numpy converter with BOOST
00594   template <typename T>
00595   void StlListToNumpy<T>::register_stllist_to_numpy_cvt() 
00596   {
00597     // Check if converter already registered for this type
00598     boost::python::type_info tinfo = boost::python::type_id< std::list<T> >();
00599     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00600     
00601     if (reg == NULL) {
00602       MsgLog(pyConverterlogger, debug,
00603              "REGISTER STL LIST<" << Traits<T>::typeName() << ">"
00604              << "TO NUMPY CONVERTER");
00605       boost::python::to_python_converter< std::list<T>, StlListToNumpy<T> >();
00606 
00607     } else if ( (*reg).m_to_python == NULL) {
00608       MsgLog(pyConverterlogger, debug,
00609              "REGISTER STL LIST<" << Traits<T>::typeName() << ">"
00610              << "TO NUMPY CONVERTER");
00611       boost::python::to_python_converter< std::list<T>, StlListToNumpy<T> >();
00612 
00613     } else {
00614       MsgLog(pyConverterlogger, debug,
00615              "STL LIST<" << Traits<T>::typeName() << ">"
00616              << "TO NUMPY CONVERTER ALREADY REGISTERED");
00617     } 
00618     
00619     return;
00620   }
00621 
00622 // ***************************************************************************
00623 // ***************************************************************************
00624 //    END OF STL-LIST<NUMBERS> TO NDARRAY CONVERTER
00625 // ***************************************************************************
00626 // ***************************************************************************
00627 
00628 
00629 
00630 
00631 
00632   // ***************************************************************************
00633   // ***************************************************************************
00634   //    START OF PYTHON-EVENT TO CPP-EVENT CONVERTER
00635   // ***************************************************************************
00636   // ***************************************************************************
00637 
00638   // Python-Event to Event convert for BOOST
00639   PyEvtToEvt& PyEvtToEvt::from_python()
00640   {
00641     // Check if converter was already registered
00642     boost::python::type_info tinfo = boost::python::type_id< boost::shared_ptr<PSEvt::Event> >();
00643     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00644 
00645     // For debugging, printing out the contents of the reg pointer will be useful. 
00646     MsgLog(pyConverterlogger, debug,"reg:" << reg);
00647     if (reg == NULL) {
00648       boost::python::converter::registry::push_back(&PyEvtToEvt::convertible,
00649                                                     &PyEvtToEvt::construct,
00650                                                     boost::python::type_id<boost::shared_ptr<PSEvt::Event> >());
00651       
00652       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Event");  
00653 
00654     } else if ((*reg).rvalue_chain == NULL && (*reg).lvalue_chain == NULL) {
00655       boost::python::converter::registry::push_back(&PyEvtToEvt::convertible,
00656                                                     &PyEvtToEvt::construct,
00657                                                     boost::python::type_id<boost::shared_ptr<PSEvt::Event> >());
00658       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Event");  
00659       // NB:When Python-Event-->Event converter is missing, both rvalue_chain and lvalue_chain are NULL
00660       // NB: the rvalue and lvalue was only checked emperically. So could be incorrect...
00661       
00662     } else {
00663       MsgLog(pyConverterlogger, debug,"BOOST PYTHON converter for Event ALREADY REGISTERED");       
00664     } 
00665     
00666     return *this;
00667   }
00668 
00669 
00670   // Check object can be converted
00671   void* PyEvtToEvt::convertible(PyObject* obj) 
00672   {
00673     MsgLog(pyConverterlogger, debug,"CHECKING PYTHON OBJECT IS A PYTHON-EVENT");
00674     MsgLog(pyConverterlogger, debug,"Pyobject type " << obj->ob_type->tp_name );
00675 
00676     if (!psana_python::Event::Object_TypeCheck(obj)) {
00677       MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS NOT A PSANA EVENT");
00678       return NULL;
00679      }
00680 
00681     MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS A PSANA EVENT");
00682     MsgLog(pyConverterlogger, debug,"Leaving convertible");
00683     return obj;
00684   }
00685 
00686 
00687 
00688 
00689 
00690   
00691   void PyEvtToEvt::construct(PyObject* obj, BoostData* boostData) 
00692   {
00693     // Reminder that BoostData is a typedef defined in the header file
00694     //  --->  typedef boost::python::converter::rvalue_from_python_stage1_data BoostData;
00695 
00696     // --> Set boostData's convertible attribute to point to the original PSANA Event object
00697     MsgLog(pyConverterlogger, debug,"Pointing back to original PSANA Event Object");
00698 
00699     // NB: WE ARE GIVING BOOST A REFERENCE TO A SMART POINTER. WE
00700     // ASSUME THAT BOOST PASSES THE SMART POINTER BY VALUE WHEN
00701     // CALLING THE C++ FUNCTION, THUS INCREMENTING THE SHARED POINTER
00702     // REFERENCE COUNT
00703     // (PYTHON CANNOT DELETE THE SHARED POINTER WHILE THIS HAPPENS)
00704 
00705     MsgLog(pyConverterlogger, debug,
00706            " WE ARE GIVING BOOST A REFERENCE TO A SMART POINTER")
00707     psana_python::Event* py_this = static_cast<psana_python::Event*>(obj);
00708     boostData->convertible = static_cast<void*> (&(py_this->m_obj));
00709 
00710     return;
00711   }
00712   // ***************************************************************************
00713   // ***************************************************************************
00714   //    END OF PYTHON-EVENT TO CPP-EVENT CONVERTER
00715   // ***************************************************************************
00716   // ***************************************************************************
00717 
00718 
00719 
00720 
00721   
00722   // ***************************************************************************
00723   // ***************************************************************************
00724   //    START OF PYTHON-ENV TO CPP-ENV CONVERTER
00725   // ***************************************************************************
00726   // ***************************************************************************
00727 
00728   // Python-Env to Env convert for BOOST
00729   PyEnvToEnv& PyEnvToEnv::from_python()
00730   {
00731     // Check if converter was already registered
00732     boost::python::type_info tinfo = boost::python::type_id< boost::shared_ptr<PSEnv::Env> >();
00733     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00734 
00735     // For debugging, printing out the contents of the reg pointer will be useful. 
00736     MsgLog(pyConverterlogger, debug,"reg:" << reg);
00737     if (reg == NULL) {
00738       boost::python::converter::registry::push_back(&PyEnvToEnv::convertible,
00739                                                     &PyEnvToEnv::construct,
00740                                                     boost::python::type_id<boost::shared_ptr<PSEnv::Env> >());
00741       
00742       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Env");  
00743 
00744     } else if ((*reg).rvalue_chain == NULL && (*reg).lvalue_chain == NULL) {
00745       boost::python::converter::registry::push_back(&PyEnvToEnv::convertible,
00746                                                     &PyEnvToEnv::construct,
00747                                                     boost::python::type_id<boost::shared_ptr<PSEnv::Env> >());
00748       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Env");  
00749       // NB:When Python-Env-->Env converter is missing, both rvalue_chain and lvalue_chain are NULL
00750       // NB: the rvalue and lvalue was only checked emperically. So could be incorrect...
00751       
00752     } else {
00753       MsgLog(pyConverterlogger, debug,"BOOST PYTHON converter for Env ALREADY REGISTERED");         
00754     } 
00755     
00756     return *this;
00757   }
00758 
00759 
00760   // Check object can be converted
00761   void* PyEnvToEnv::convertible(PyObject* obj) 
00762   {
00763     MsgLog(pyConverterlogger, debug,"CHECKING PYTHON OBJECT IS A PYTHON-ENV");
00764     MsgLog(pyConverterlogger, debug,"Pyobject type " << obj->ob_type->tp_name );
00765 
00766     if (!psana_python::Env::Object_TypeCheck(obj)) {
00767       MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS NOT A PSANA ENV");
00768       return NULL;
00769      }
00770 
00771     MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS A PSANA ENV");
00772     MsgLog(pyConverterlogger, debug,"Leaving convertible");
00773     return obj;
00774   }
00775 
00776 
00777 
00778 
00779 
00780   
00781   void PyEnvToEnv::construct(PyObject* obj, BoostData* boostData) 
00782   {
00783     // Reminder that BoostData is a typedef defined in the header file
00784     //  --->  typedef boost::python::converter::rvalue_from_python_stage1_data BoostData;
00785 
00786     // --> Set boostData's convertible attribute to point to the original PSANA Env object
00787     MsgLog(pyConverterlogger, debug,"Pointing back to original PSANA Env Object");
00788 
00789 
00790     // NB: WE ARE GIVING BOOST A REFERENCE TO A SMART POINTER. WE
00791     // ASSUME THAT BOOST PASSES THE SMART POINTER BY VALUE WHEN
00792     // CALLING THE C++ FUNCTION, THUS INCREMENTING THE SHARED POINTER
00793     // REFERENCE COUNT
00794     // (PYTHON CANNOT DELETE THE SHARED POINTER WHILE THIS HAPPENS)
00795     MsgLog(pyConverterlogger, debug,
00796            " WE ARE GIVING BOOST A REFERENCE TO A SMART POINTER")      
00797     psana_python::Env* py_this = static_cast<psana_python::Env*>(obj);
00798     boostData->convertible = static_cast<void*> (&(py_this->m_obj));
00799 
00800     return;
00801   }
00802   // ***************************************************************************
00803   // ***************************************************************************
00804   //    END OF PYTHON-ENV TO CPP-ENV CONVERTER
00805   // ***************************************************************************
00806   // ***************************************************************************
00807 
00808 
00809 
00810 
00811 
00812   // ***************************************************************************
00813   // ***************************************************************************
00814   //    START OF PYTHON-SOURCE TO CPP-SOURCE CONVERTER
00815   // ***************************************************************************
00816   // ***************************************************************************
00817 
00818   // Python-Source to Source convert for BOOST
00819   PySourceToSource& PySourceToSource::from_python()
00820   {
00821     // Check if converter was already registered
00822     boost::python::type_info tinfo = boost::python::type_id<PSEvt::Source>();
00823     boost::python::converter::registration const* reg = boost::python::converter::registry::query(tinfo);
00824 
00825     // For debugging, printing out the contents of the reg pointer will be useful. 
00826     MsgLog(pyConverterlogger, debug,"reg:" << reg);
00827     if (reg == NULL) {
00828       boost::python::converter::registry::push_back(&PySourceToSource::convertible,
00829                                                     &PySourceToSource::construct,
00830                                                     boost::python::type_id<PSEvt::Source>());
00831                                                           
00832       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Source");  
00833 
00834     } else if ((*reg).rvalue_chain == NULL && (*reg).lvalue_chain == NULL) {
00835       boost::python::converter::registry::push_back(&PySourceToSource::convertible,
00836                                                     &PySourceToSource::construct,
00837                                                     boost::python::type_id<PSEvt::Source>());
00838       MsgLog(pyConverterlogger, debug,"REGISTER BOOST PYTHON converter for Source");  
00839       // NB:When Python-Source-->Source converter is missing, both rvalue_chain and lvalue_chain are NULL
00840       // NB: the rvalue and lvalue was only checked emperically. So could be incorrect...
00841       
00842     } else {
00843       MsgLog(pyConverterlogger, debug,"BOOST PYTHON converter for Source ALREADY REGISTERED");      
00844     } 
00845     
00846     return *this;
00847   }
00848 
00849 
00850   // Check object can be converted
00851   void* PySourceToSource::convertible(PyObject* obj) 
00852   {
00853     MsgLog(pyConverterlogger, debug,"CHECKING PYTHON OBJECT IS A PYTHON-SOURCE");
00854 
00855     if (!psana_python::Source::Object_TypeCheck(obj) ) {
00856       MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS NOT A PSANA SOURCE");
00857       return NULL;
00858     }
00859 
00860     MsgLog(pyConverterlogger, debug,"PYTHON OBJECT IS A PSANA SOURCE");
00861     MsgLog(pyConverterlogger, debug,"Leaving convertible");
00862     return obj;
00863   }
00864 
00865 
00866 
00867 
00868 
00869   
00870   void PySourceToSource::construct(PyObject* obj, BoostData* boostData) 
00871   {
00872     // Reminder that BoostData is a typedef defined in the header file
00873     //  --->  typedef boost::python::converter::rvalue_from_python_stage1_data BoostData;
00874 
00875     // --> Set boostData's convertible attribute to point to the original PSANA Source object
00876     MsgLog(pyConverterlogger, debug,"Pointing back to original PSANA Source Object");
00877 
00878     psana_python::Source* py_this = static_cast<psana_python::Source*>(obj);
00879     
00880     
00881     // Get pointer to the converter's allocated memory block for the
00882     // outgoing CPP-SOURCE
00883     // --> first create typedef storagetype for convenience
00884     typedef boost::python::converter::rvalue_from_python_storage<PSEvt::Source> storagetype;
00885     // --> Now grab the pointer that BOOST has allocated to store the
00886     // --> CPP-SOURCE
00887     void* storage = reinterpret_cast<storagetype*> (boostData)->storage.bytes;
00888 
00889     // --> Now set data's convertible attribute to outgoing CPP-SOURCE
00890     MsgLog(pyConverterlogger, debug,"Creating outgoing CPP-SOURCE");
00891     boostData->convertible = new(storage) PSEvt::Source(py_this->m_obj);
00892 
00893     return;
00894   }
00895   // ***************************************************************************
00896   // ***************************************************************************
00897   //    END OF PYTHON-SOURCE TO CPP-SOURCE CONVERTER
00898   // ***************************************************************************
00899   // ***************************************************************************
00900 
00901 
00902 
00903 
00904 
00905 }  // end of psana_python namespace
00906 

Generated on 19 Dec 2016 for PSDMSoftware by  doxygen 1.4.7