Commit a0bdbee8 authored by Mozul Rémy's avatar Mozul Rémy
Browse files

splitting test in two directories

parent aa2e0104
Utilisation : Directory organisation
make ----------------------
python pytest
logilab_sprint : directory contains the source of the first test written
during a sprint with logilab.
mod_type.f90 : description du type bind(c) fortran_copied_db : extend previous example with complex data structure
type.h : header associé a mod_type.f90 to illustrate technical principal. It also presents
the first pitfall by doing copy without wanting it
on data which are not explicitely pointers on C side
fortran_shared_db : a modification of previous example to really share
memory of every field of the data structure
ctest.pxd : equivalent d'un header pour cython The following link has been used to instanciate a numpy array referencing
(voir si on peut faire une génération auto) a fortran allocated array :
test.pyx : module p/cython implémentant la classe python https://gist.github.com/1249305/bd8a6922507b7e5da0f1417fdba77d5115dd12d4
wrappant le contenu du .pxd
pytest.py : le test
Pour comment créer une vue numpy d'un pointeur fortran : Use and tests
https://gist.github.com/1249305/bd8a6922507b7e5da0f1417fdba77d5115dd12d4 -------------
Currently there is makefile in each directory.
There should be an example in each directory !
Dependencies
------------
A Fortran compiler implementing the iso_c_binding standard.
Cython and Python. Tested with:
- gfortran 4.8.2
- python 2.7
- numpy 1.8.0
- cython 0.20
To do
-----
- Use cmake in one of the directories
- Try with python3
- Sort how to reference properly the content of "weird" arrays
.. mod_type.f90 : description du type bind(c)
.. type.h : header associé a mod_type.f90
..
.. ctest.pxd : equivalent d'un header pour cython
.. (voir si on peut faire une génération auto)
.. test.pyx : module p/cython implémentant la classe python
.. wrappant le contenu du .pxd
..
.. pytest.py : le test
typedef struct
{
int itype;
double val;
char field[30];
} C_param;
typedef struct
{
char nickname[30];
double rsingle;
int isingle;
int itab[2]; //contains sizes of rtab and weird in that order
double * rtab;
C_param * weird;
} C_datatype;
void getOne(int id, C_datatype * d);
void displayOne(int id);
void changeOne(int id, char * nn, double rv, int iv);
cdef extern from 'dtype.h':
ctypedef struct C_param:
int itype
double val
char field[30]
ctypedef struct C_datatype:
char nickname[30]
double rsingle
int isingle
int itab[2] #contain sizes of rtab and weird in that order
double * rtab
C_param * weird
void getOne(int id, C_datatype * d)
void displayOne(int id)
void changeOne(int id, char * nn, double rv, int iv)
cimport numpy as np
import numpy as np
import cython as ct
from libc.string cimport strcpy
from dtype cimport C_param, C_datatype, getOne, displayOne, changeOne
np.import_array()
cdef class P_datatype(object):
cdef int id
cdef C_datatype f_image
cdef public np.ndarray rtab
@ct.locals(i=int)
def __init__(self, i):
self.id = i
getOne(i,&self.f_image)
# convenient to store the numpy array structure around the pointer
self.rtab = np.PyArray_SimpleNewFromData(1, [self.f_image.itab[0]], np.NPY_DOUBLE, self.f_image.rtab)
# beurk
self.f_image.nickname[30] = '\0'
for i in range(self.f_image.itab[1]):
self.f_image.weird[i].field[30] = '\0'
def fortranContent(self):
displayOne(self.id)
def fortranChange(self,nn,rv,iv):
changeOne(self.id, nn, rv, iv)
def cythonContent(self):
cdef int i
print self.f_image.nickname
print self.f_image.rsingle
print self.f_image.isingle
for i in range(self.f_image.itab[0]):
print self.f_image.rtab[i]
print self.rtab
for i in range(self.f_image.itab[1]):
print self.f_image.weird[i]
def cythonChange(self,nn,rv,iv):
#this is not possible because char[30] is a non-lvalue
#self.f_image.nickname = nn
strcpy(self.f_image.nickname, nn)
self.f_image.rsingle = rv
self.f_image.isingle = iv
def getWeirdCopy(self,i):
#as usual careful between Fortran/Python numbering
if 0 < i <= self.f_image.itab[1]:
return self.f_image.weird[i-1]
else:
raise IndexError
all: dtype.so
libdtype.so: mod_dtype.f90
gfortran -shared -o $@ $<
dtype.so: dtype.pyx libdtype.so
python setup.py build_ext -i
clean:
@rm -f *.so *.c *.mod
module data_type
use iso_c_binding
implicit none
private
! an intermediate type
type, bind(c) :: T_param
integer(kind=c_int) :: itype
real(kind=c_double) :: val
character(kind=c_char,len=30) :: field
end type T_param
! the real type to bind
type, bind(c) :: T_datatype
! a string of size 30
character(kind=c_char,len=30) :: nickname
! a real alone
real(kind=c_double) :: rsingle
! an integer alone
integer(kind=c_int) :: isingle
! a fixed size array :
! contains the sizes of rtab and weird arrays in that order
integer(kind=c_int), dimension(2) :: itab
! a dynamic array
! doesn't work like this : real(kind=c_double), dimension(:), pointer :: rtab
type(c_ptr) :: rtab
! a pointer on an array of an interoperable type
!type(T_param), dimension(:), pointer :: weird => null()
type(c_ptr) :: weird
end type T_datatype
type(T_datatype), dimension(:), allocatable :: mydatas
!----------------------------------------------------------------------
integer :: nb_data = 0
! --------------------------
! subroutines set to private
! --------------------------
private init
! -------------------------
! subroutines set to public
! -------------------------
! wrap API
public get_one, display_one, change_one
contains
subroutine init()
logical, save :: is_first = .true.
integer(kind=4) :: i, j
real(kind=8) , dimension(:), pointer :: rtab
type(T_param), dimension(:), pointer :: weird
rtab => null()
weird => null()
if( is_first ) then
nb_data = 5
allocate(mydatas(nb_data))
do i = 1, nb_data
write(mydatas(i)%nickname,'(A,I0,A)') "data ", i, " in fortran"
mydatas(i)%rsingle = i*0.1d0
mydatas(i)%isingle = i
mydatas(i)%itab = (/i,i-1/)
allocate(rtab(i))
rtab = (/0.1d0,0.2d0/)
mydatas(i)%rtab = c_loc(rtab(1))
allocate(weird(i-1))
do j = 1, i-1
weird(j)%itype = j
weird(j)%val = 0.01d0*j
write(weird(j)%field,'(A,I0,A,I0)') 'field of ',j,' in ', i
end do
mydatas(i)%weird = c_loc(weird(1))
end do
is_first = .false.
end if
end subroutine
subroutine get_one(id, d) bind(c, name='getOne')
implicit none
integer(kind=c_int), intent(in), value :: id
type(T_datatype) :: d
call init()
if( id > nb_data ) then
write(*,*) 'error index'
return
end if
d = mydatas(id)
end subroutine
subroutine display_one(id) bind(c, name='displayOne')
implicit none
integer(kind=c_int), intent(in), value :: id
!
integer(kind=4) :: i
real(kind=8) , dimension(:), pointer :: rtab
type(T_param), dimension(:), pointer :: weird
if( id > nb_data ) then
write(*,*) 'error index'
return
end if
print *,'T_DATA of id ', id
print *,' nickname : ', mydatas(id)%nickname
print *,' rsingle : ', mydatas(id)%rsingle
print *,' isingle : ', mydatas(id)%isingle
print *,' itab : ', mydatas(id)%itab
call c_f_pointer(mydatas(id)%rtab,rtab,(/mydatas(id)%itab(1)/))
call c_f_pointer(mydatas(id)%weird,weird,(/mydatas(id)%itab(2)/))
print *,' rtab : ', rtab
do i = 1, mydatas(id)%itab(2)
print *,' weird : ', i
print *,' --itype : ', weird(i)%itype
print *,' --val : ', weird(i)%val
print *,' --field : ', weird(i)%field
end do
end subroutine
subroutine change_one(id, nn, rv, iv) bind(c, name='changeOne')
implicit none
integer(kind=c_int), intent(in), value :: id
real(kind=c_double), intent(in), value :: rv
integer(kind=c_int), intent(in), value :: iv
type(c_ptr), value :: nn
!
character(kind=c_char,len=30), pointer :: nname
integer(kind=4) :: i, j
call c_f_pointer(cptr=nn,fptr=nname)
i = 1
do while( i <30 .and. nname(i:i) /= c_null_char )
i = i + 1
end do
mydatas(id)%nickname(1:i) = nname(1:i)
mydatas(id)%nickname(i+1:) = ''
mydatas(id)%rsingle = rv
mydatas(id)%isingle = iv
end subroutine
end module data_type
import os
import numpy
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup(name='sprint',
ext_modules=[Extension('dtype',['dtype.pyx'],
libraries=['dtype'],
library_dirs=[os.getcwd()],
include_dirs=[numpy.get_include()])],
cmdclass={'build_ext':build_ext}
)
import dtype
print('------------------------------------------')
print('initializing array and getting 2nd element')
dat = dtype.P_datatype(2)
dat.fortranContent()
dat.cythonContent()
print('------------------------------------------')
print('call to fortranChange with parameters \'fortran change\', 13. 6')
dat.fortranChange('fortran change', 13.,6)
print('------------------------------------------')
print('changing content of dat.rtab[0] = -8000.')
dat.rtab[0] = -8000.
dat.cythonContent()
print('------------------------------------------')
print('call to cythonChange with parameters \'cython change\', 144. 12')
dat.cythonChange('cython change', 144., 12)
dat.fortranContent()
print('------------------------------------------')
print('getting a weird strucutre as a dictionnary and printing key:val')
#getting a weird structure seen
#as a dictionary
di2_1 = dat.getWeirdCopy(1)
for k in di2_1.keys():
print(str(k)+':'+str(di2_1[k]))
print('------------------------------------------')
# to check handling of null pointers
# within the data structures
print('checking handling of null pointers')
d0 = dtype.P_datatype(1)
d0.fortranContent()
d0.cythonContent()
...@@ -20,8 +20,6 @@ typedef struct ...@@ -20,8 +20,6 @@ typedef struct
} C_datatype; } C_datatype;
void getOne(int id, C_datatype * d);
C_datatype * addOne(char * nn, double rv, int iv); C_datatype * addOne(char * nn, double rv, int iv);
void displayOne(C_datatype * data);
void display(int id); void changeOne(C_datatype * data, char * nn, double rv, int iv);
void change(int id, char * nn, double rv, int iv);
...@@ -14,8 +14,6 @@ cdef extern from 'dtype.h': ...@@ -14,8 +14,6 @@ cdef extern from 'dtype.h':
double * rtab double * rtab
C_param * weird C_param * weird
void getOne(int id, C_datatype * d)
C_datatype * addOne(char * nn, double rv, int iv) C_datatype * addOne(char * nn, double rv, int iv)
void displayOne(C_datatype * d)
void display(int id) void changeOne(C_datatype * d, char * nn, double rv, int iv)
void change(int id, char * nn, double rv, int iv)
...@@ -5,66 +5,34 @@ import numpy as np ...@@ -5,66 +5,34 @@ import numpy as np
import cython as ct import cython as ct
from libc.string cimport strcpy from libc.string cimport strcpy
from dtype cimport C_param, C_datatype, getOne, display, change from dtype cimport C_param, C_datatype, addOne, displayOne, changeOne
np.import_array() np.import_array()
cdef class P_datatype(object): cdef class P_datatype(object):
cdef int id cdef int id
cdef C_datatype f_image
cdef C_datatype * f_image_ptr cdef C_datatype * f_image_ptr
cdef public np.ndarray rtab cdef public np.ndarray rtab
@ct.locals(i=int) @ct.locals(i=int)
def __init__(self, i,nn,rv,iv): def __init__(self,nn,rv,iv):
self.id = i
getOne(i,&self.f_image)
self.rtab = np.PyArray_SimpleNewFromData(1, [self.f_image.itab[0]], np.NPY_DOUBLE, self.f_image.rtab)
self.f_image_ptr = addOne(nn, rv, iv) self.f_image_ptr = addOne(nn, rv, iv)
self.rtab = np.PyArray_SimpleNewFromData(1, [self.f_image_ptr.itab[0]], np.NPY_DOUBLE, self.f_image_ptr.rtab)
# beurk # beurk
self.f_image.nickname[30] = '\0'
for i in range(self.f_image.itab[1]):
self.f_image.weird[i].field[30] = '\0'
self.f_image_ptr.nickname[30] = '\0' self.f_image_ptr.nickname[30] = '\0'
for i in range(self.f_image_ptr.itab[1]): for i in range(self.f_image_ptr.itab[1]):
self.f_image_ptr.weird[i].field[30] = '\0' self.f_image_ptr.weird[i].field[30] = '\0'
def cythonChange(self,nn,rv,iv): def fortranContent(self):
#this is not possible because char[30] is a non-lvalue displayOne(self.f_image_ptr)
#self.f_image.nickname = nn
strcpy(self.f_image.nickname, nn)
self.f_image.rsingle = rv
self.f_image.isingle = iv
strcpy(self.f_image_ptr.nickname, nn)
self.f_image_ptr.rsingle = rv
self.f_image_ptr.isingle = iv
def fortranChange(self,nn,rv,iv): def fortranChange(self,nn,rv,iv):
change(self.id, nn, rv, iv) changeOne(self.f_image_ptr, nn, rv, iv)
def getWeirdCopy(self,i):
if i < self.f_image.itab[1]:
return self.f_image.weird[i]
else:
raise IndexError
def fortranContent(self):
display(self.id)
def cythonContent(self): def cythonContent(self):
print 'f_image :'
cdef int i
print self.f_image.nickname
print self.f_image.rsingle
print self.f_image.isingle
for i in range(self.f_image.itab[0]):
print self.f_image.rtab[i]
print self.rtab
for i in range(self.f_image.itab[1]):
print self.f_image.weird[i]
print 'f_image_ptr :' print 'f_image_ptr :'
print self.f_image_ptr.nickname print self.f_image_ptr.nickname
print self.f_image_ptr.rsingle print self.f_image_ptr.rsingle
...@@ -74,3 +42,17 @@ cdef class P_datatype(object): ...@@ -74,3 +42,17 @@ cdef class P_datatype(object):
for i in range(self.f_image_ptr.itab[1]): for i in range(self.f_image_ptr.itab[1]):
print self.f_image_ptr.weird[i] print self.f_image_ptr.weird[i]
def cythonChange(self,nn,rv,iv):
#this is not possible because char[30] is a non-lvalue
#self.f_image_ptr.nickname = nn
strcpy(self.f_image_ptr.nickname, nn)
self.f_image_ptr.rsingle = rv
self.f_image_ptr.isingle = iv
def getWeirdCopy(self,i):
#as usual careful between Fortran/Python numbering
if 0 < i <= self.f_image_ptr.itab[1]:
return self.f_image_ptr.weird[i-1]
else:
raise IndexError
...@@ -31,16 +31,14 @@ module data_type ...@@ -31,16 +31,14 @@ module data_type
! a dynamic array ! a dynamic array
! doesn't work like this : real(kind=c_double), dimension(:), pointer :: rtab ! doesn't work like this : real(kind=c_double), dimension(:), pointer :: rtab
type(c_ptr) :: rtab type(c_ptr) :: rtab = c_null_ptr
! a pointer on an array of an interoperable type ! a pointer on an array of an interoperable type
!type(T_param), dimension(:), pointer :: weird => null() !type(T_param), dimension(:), pointer :: weird => null()
type(c_ptr) :: weird type(c_ptr) :: weird = c_null_ptr
end type T_datatype end type T_datatype
type(T_datatype), dimension(:), allocatable :: mydatas
type T_dt_link type T_dt_link
type(t_datatype), pointer :: d => null() type(t_datatype), pointer :: d => null()
type(T_dt_link) , pointer :: n => null() type(T_dt_link) , pointer :: n => null()
...@@ -64,45 +62,10 @@ module data_type ...@@ -64,45 +62,10 @@ module data_type
! wrap API ! wrap API
public get_one public add_one, display_one, change_one
contains contains
subroutine get_one(id, d) bind(c, name='getOne')
implicit none
integer(kind=c_int), intent(in), value :: id
type(T_datatype) :: d
!
logical, save :: is_first = .true.
integer(kind=4) :: i
real(kind=8) , dimension(:), pointer :: rtab
type(T_param), dimension(:), pointer :: weird
if(