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

splitting test in two directories

parent aa2e0104
Utilisation :
make
python pytest
Directory organisation
----------------------
logilab_sprint : directory contains the source of the first test written
during a sprint with logilab.
mod_type.f90 : description du type bind(c)
type.h : header associé a mod_type.f90
fortran_copied_db : extend previous example with complex data structure
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
(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
The following link has been used to instanciate a numpy array referencing
a fortran allocated array :
https://gist.github.com/1249305/bd8a6922507b7e5da0f1417fdba77d5115dd12d4
pytest.py : le test
Pour comment créer une vue numpy d'un pointeur fortran :
https://gist.github.com/1249305/bd8a6922507b7e5da0f1417fdba77d5115dd12d4
Use and tests
-------------
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
} C_datatype;
void getOne(int id, C_datatype * d);
C_datatype * addOne(char * nn, double rv, int iv);
void display(int id);
void change(int id, char * nn, double rv, int iv);
void displayOne(C_datatype * data);
void changeOne(C_datatype * data, char * nn, double rv, int iv);
......@@ -14,8 +14,6 @@ cdef extern from 'dtype.h':
double * rtab
C_param * weird
void getOne(int id, C_datatype * d)
C_datatype * addOne(char * nn, double rv, int iv)
void display(int id)
void change(int id, char * nn, double rv, int iv)
void displayOne(C_datatype * d)
void changeOne(C_datatype * d, char * nn, double rv, int iv)
......@@ -5,66 +5,34 @@ import numpy as np
import cython as ct
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()
cdef class P_datatype(object):
cdef int id
cdef C_datatype f_image
cdef C_datatype * f_image_ptr
cdef public np.ndarray rtab
@ct.locals(i=int)
def __init__(self, i,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)
def __init__(self,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
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'
for i in range(self.f_image_ptr.itab[1]):
self.f_image_ptr.weird[i].field[30] = '\0'
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
strcpy(self.f_image_ptr.nickname, nn)
self.f_image_ptr.rsingle = rv
self.f_image_ptr.isingle = iv
def fortranContent(self):
displayOne(self.f_image_ptr)
def fortranChange(self,nn,rv,iv):
change(self.id, 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)
changeOne(self.f_image_ptr, nn, rv, iv)
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 self.f_image_ptr.nickname
print self.f_image_ptr.rsingle
......@@ -74,3 +42,17 @@ cdef class P_datatype(object):
for i in range(self.f_image_ptr.itab[1]):
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
! a dynamic array
! 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
!type(T_param), dimension(:), pointer :: weird => null()
type(c_ptr) :: weird
type(c_ptr) :: weird = c_null_ptr
end type T_datatype
type(T_datatype), dimension(:), allocatable :: mydatas
type T_dt_link
type(t_datatype), pointer :: d => null()
type(T_dt_link) , pointer :: n => null()
......@@ -64,45 +62,10 @@ module data_type
! wrap API
public get_one
public add_one, display_one, change_one
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( is_first ) then
allocate(mydatas(1))
mydatas(1)%nickname = "data in fortran"
mydatas(1)%rsingle = 1.d0
mydatas(1)%isingle = 1
mydatas(1)%itab = (/2,3/)
allocate(rtab(2))
rtab = (/0.1d0,0.2d0/)
mydatas(1)%rtab = c_loc(rtab(1))
allocate(weird(3))
do i = 1, 3
weird(i)%itype = i
weird(i)%val = 1.
write(weird(i)%field,'(A,I0)') 'field of ',i
end do
mydatas(1)%weird = c_loc(weird(1))
is_first = .false.
end if
d = mydatas(1)
end subroutine
function add_one(nn,rv,iv) bind(c, name='addOne')
implicit none
real(kind=c_double), intent(in), value :: rv
......@@ -116,8 +79,9 @@ contains
integer(kind=4) :: i
type(T_dt_link), pointer :: new
allocate(new)
allocate(new%d)
rtab => null()
weird => null()
new => null()
call c_f_pointer(cptr=nn,fptr=nname)
......@@ -125,85 +89,69 @@ contains
do while( i <30 .and. nname(i+1:i+1) /= c_null_char )
i = i + 1