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!