Features everywhere and a basic example

In the past 2 weeks, we’ve been able to put in some major front-facing features — features that make fwrapped python modules friendly (or, at least, friendlier) and discoverable.

The most significant features for users are module- and function-level docstrings, but there are many more features you’ll like.

For this trivial fortran function:

function one_arg(a)
    implicit none
    ! Automatic type discovery: the C type correspondances
    ! will be automatically determined at compile-time.
    integer :: one_arg
    ! Assumed-shape arrays handled seamlessly
    real(kind=8), dimension(:, : ), intent(inout) :: a

    a = 1.61803399_8
    ! bonus if you know 1.618... without googling
    one_arg = 42

end function

Fwrapping it:

$ fwrapc source.f90 --build \
--name=one_arg --fcompiler=gnu95 \
--f90exec=/usr/bin/gfortran-4.3 \
-L/usr/lib/gcc/x86_64-linux-gnu/4.3.2 -lgfortran

(Admittedly the commandline can get kinda long, but the -L, -l and --f90exec options can be set to environment options $LDFLAGS and $F90, resp. The fwrapc command needs to know where to find the fortran compiler and how to link in the fortran runtime libraries.)

This will generate a million files in ./one_arg, including an extension module, ./one_arg/one_arg.so. All those other files are useful if and when you want to use the wrappers from C or Cython. For now we’ll look at using the code from Python. Importing that extension module, we see some interesting stuff:

$ cd one_arg
$ ipython
Python 2.5.2 (r252:60911, Jan 24 2010, 17:44:40) 
Type "copyright", "credits" or "license" for more information.

IPython 0.10 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: import one_arg

In [2]: one_arg?
Type:		module
Base Class:	<type 'module'>
String Form:	<module 'one_arg' from 'one_arg.so'>
Namespace:	Interactive
File:		/home/ksmith/Devel/fwrap/fwrap-dev/testdir/one_arg/one_arg.so
Docstring:
    The one_arg module was generated with Fwrap v0.1.0dev_a0bf087f24a0+.
    
    Below is a listing of functions and data types.
    For usage information see the function docstrings.
    
    Functions
    ---------
    one_arg(...)
    
    Data Types
    ----------
    fw_character
    fwi_integer
    fwi_npy_intp
    fwr_real_8


In [3]:

You’ll notice a list of functions and data types in the one_arg module. The datatypes fwr_real_8 and fwi_integer correspond to the function argument and return value types, resp. (automatic type-discovery, like I mentioned).

We can take a look at the one_arg.one_arg() docstring:

In [3]: one_arg.one_arg?
Type:		builtin_function_or_method
Base Class:	<type 'builtin_function_or_method'>
String Form:	<built-in function one_arg>
Namespace:	Interactive
Docstring:
    one_arg(a) -> (fw_ret_arg, a,)
    
    Parameters
    ----------
    a : fwr_real_8, 2D array, dimension(:, : ), intent inout
    
    Returns
    -------
    fw_ret_arg : fwi_integer, intent out
    a : fwr_real_8, 2D array, dimension(:, : ), intent inout


In [4]: 

It tells us the function signature, the argument types and the return tuples.

Let’s kick the tires:

In [4]: one_arg.one_arg([[1,2,3], [4,5,6]])
Out[4]: 
(42,
 array([[ 1.61803399,  1.61803399,  1.61803399],
       [ 1.61803399,  1.61803399,  1.61803399]]))

And we find that the fortran function return value is the first element of the return tuple, and the array is the second. In this case, we passed in a python list, and fwrap made an internal copy seeing that it wasn’t compatible with the type and ordering of the fortran dummy argument. If we pass in an array of the proper ordering and type, no copy will be made, and it will be modified in-place (there will be options to have finer control over this behavior, including warnings or exceptions raised if you don’t want any copies made, ever):

In [5]: import numpy as np

In [6]: a = np.empty((5,5), dtype=one_arg.fwr_real_8, order='F')

In [7]: one_arg.one_arg(a)
Out[7]: 
(42,
 array([[ 1.61803399,  1.61803399,  1.61803399,  1.61803399,  1.61803399],
       [ 1.61803399,  1.61803399,  1.61803399,  1.61803399,  1.61803399],
       [ 1.61803399,  1.61803399,  1.61803399,  1.61803399,  1.61803399],
       [ 1.61803399,  1.61803399,  1.61803399,  1.61803399,  1.61803399],
       [ 1.61803399,  1.61803399,  1.61803399,  1.61803399,  1.61803399]]))

In [8]: (ret, a_ret) = one_arg.one_arg(a)

In [9]: a_ret is a
Out[9]: True

So a_ret and a are the same python object, no copying took place!

I hope this was instructive. A release is coming soon!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: