Calling Fortran from Python

M

Mangabasi

Howdy,

I have been trying to call the following Fortran function from Python
(using Windows XP, Compaq Fortran and Python 2.4). I tried F2Py,
Pyfort and calldll with no success.

I think I came very close with calldll. Here is a short summary

Fortran code:

SUBROUTINE SAMPLE(IERR1,IERR2,AIN,AOUT)
C
C SIMPLE SAMPLE OF A DLL
C
!DEC$ATTRIBUTES DLLEXPORT :: SAMPLE
!DEC$ATTRIBUTES ALIAS:'SAMPLE' :: SAMPLE
INTEGER,INTENT(OUT) :: IERR1,IERR2
REAL*8,INTENT(IN) :: AIN(*)
REAL*8,INTENT(OUT) :: AOUT(*)
C
C *** SET MAXIMUM EXPECTED ELEMENTS OF ARRAY AIN AND AOUT
C
PARAMETER (MAXNVIN=101,MAXNVOUT=200)
C
C *** SET ERROR INDICATORS TO ZERO
C
IERR1=0
IERR2=0
C
C *** GET NUMBER OF INPUT VALUES
C
NVIN=AIN(1)
C *** IF NUMBER EXCEEDS MAXIMUM EXPECTED SET ERRORS AND RETURN
IF(NVIN .GT. MAXNVIN) THEN
IERR1=1
IERR2=1
RETURN
ENDIF
C
C *** SET NUMBER OF OUTPUT VALUES
C
NVOUT=2*NVIN
C *** IF NUMBER EXCEEDS MAXIMUM EXPECTED SET ERRORS AND RETURN
IF(NVOUT .GT. MAXNVOUT) THEN
IERR1=1
IERR2=2
RETURN
ENDIF
C
C *** INITIALIZE AOUT INDEX
C
JOUT=0
C
C *** COMPUTE TWO OUTPUT VALUES FOR EACH INPUT VALUE
C
DO I=1,NVIN
JOUT=JOUT+1
AOUT(JOUT)=2.*AIN(I+1)
JOUT=JOUT+1
AOUT(JOUT)=3.*AIN(I+1)
END DO
RETURN
END

compiled it to a dll and called this dll from another Fortran program
with success, so this tells me that dll is OK.

This is how I tried to call it from python 2.4

import calldll
handle = calldll.load_library('c:/sample_dll.dll')
addr = calldll.get_proc_address(handle, 'SAMPLE')
#so far so good, I got a handle and address
e1 = 0
e2 = 0
ain = [2, 3, 4]
aout = [ ]
calldll.call_foreign_function(addr, 'hhll', 'l',(e1, e2,ain,aout))
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: an integer is required

Has anyone provide a similar example with Pyfort, F2Py or calldll?

Thanks in advance.
 
R

Robert Kern

Mangabasi said:
Has anyone provide a similar example with Pyfort, F2Py or calldll?

With the latest f2py in numpy:

$ cat sample.pyf
! -*- f90 -*-
! Note: the context of this file is case sensitive.

python module sample ! in
interface ! in :sample
subroutine sample(ierr1,ierr2,ain,aout) ! in :sample:sample.f
integer intent(out) :: ierr1
integer intent(out) :: ierr2
real*8 dimension(*),intent(in) :: ain

!! This is what I had to modify from the generated interface. I
!! changed 'dimension(*)' to 'dimension(len(ain))'.
real*8 dimension(len(ain)),intent(out) :: aout
end subroutine sample
end interface
end python module sample

! This file was auto-generated with f2py (version:2_3582).
! See http://cens.ioc.ee/projects/f2py2e/

]$ f2py -c -m sample sample.pyf sample.f

running build
running config_fc
running build_src
building extension "sample" sources
creating /tmp/tmpZL8qAw
creating /tmp/tmpZL8qAw/src.macosx-10.3-fat-2.5
f2py options: []
f2py: sample.pyf
Reading fortran codes...
Reading file 'sample.pyf' (format:free)
Post-processing...
Block: sample
Block: sample
Post-processing (stage 2)...
Building modules...
Building module "sample"...
Constructing wrapper function "sample"...
ierr1,ierr2,aout = sample(ain)
Wrote C/API module "sample" to file
"/tmp/tmpZL8qAw/src.macosx-10.3-fat-2.5/samplemodule.c"
adding '/tmp/tmpZL8qAw/src.macosx-10.3-fat-2.5/fortranobject.c' to sources.
adding '/tmp/tmpZL8qAw/src.macosx-10.3-fat-2.5' to include_dirs.

.... Etc.

$ ipython
Activating auto-logging. Current session state plus future input saved.
Filename : /Users/rkern/.ipython/ipython.log
Mode : backup
Output logging : False
Raw input log : False
Timestamping : False
State : active

In [1]: import sample

In [2]: sample.sample([1, 2, 3])
Out[2]: (0, 0, array([ 4., 6., 0.]))


--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
M

Mangabasi

Robert,

Thanks for your prompt response. I think I got a lot closer but no
cigar yet.

This is the output

C:\fortrandll>f2py -c -m sample sample.pyf sample.for
numpy_info:
FOUND:
define_macros = [('NUMERIC_VERSION', '"\\"24.2\\""')]
include_dirs = ['C:\\Python24\\include']

running build
running config_fc
running build_src
building extension "sample" sources
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv\src
f2py: sample.pyf
Reading fortran codes...
Reading file 'sample.pyf'
Post-processing...
Block: sample
Block: sample
Post-processing (stage 2)...
Building modules...
Building module "sample"...
Constructing wrapper function "sample"...
ierr1,ierr2,aout = sample(ain)
Wrote C/API module "sample" to file "c:\docume~1\fb
\locals~1\temp\tmpcos
vgv\src/samplemodule.c"
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src\fortranobject.c'
to sources
..
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src' to include_dirs.
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.c -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.h -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
running build_ext
No module named msvccompiler in scipy_distutils, trying from
distutils..
error: The .NET Framework SDK needs to be installed before building
extensions f
or Python.

I think I have a problem with distutils' msvccompiler.py. It may be
the MacroExpander in distutils guessing the visual studio path
incorrectly or something related to the registry keys. Is there a way
to specify the C compiler path to f2py so that it does not rely on the
distutils? Or maybe I am totally off base here, I don't know.

Any thoughts?
 
M

Mangabasi

There may be a way to finish this without having to deal with
distutils.
F2py created three files so far

samplemodule.c
fortranobject.h
fortranobject.c

Is there a way to create the sample.pyd from these files?
 
R

Robert Kern

Mangabasi said:
Robert,

Thanks for your prompt response. I think I got a lot closer but no
cigar yet.

This is the output

C:\fortrandll>f2py -c -m sample sample.pyf sample.for
numpy_info:
FOUND:
define_macros = [('NUMERIC_VERSION', '"\\"24.2\\""')]
include_dirs = ['C:\\Python24\\include']

running build
running config_fc
running build_src
building extension "sample" sources
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv\src
f2py: sample.pyf
Reading fortran codes...
Reading file 'sample.pyf'
Post-processing...
Block: sample
Block: sample
Post-processing (stage 2)...
Building modules...
Building module "sample"...
Constructing wrapper function "sample"...
ierr1,ierr2,aout = sample(ain)
Wrote C/API module "sample" to file "c:\docume~1\fb
\locals~1\temp\tmpcos
vgv\src/samplemodule.c"
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src\fortranobject.c'
to sources
.
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src' to include_dirs.
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.c -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.h -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
running build_ext
No module named msvccompiler in scipy_distutils, trying from
distutils..
error: The .NET Framework SDK needs to be installed before building
extensions f
or Python.

I think I have a problem with distutils' msvccompiler.py. It may be
the MacroExpander in distutils guessing the visual studio path
incorrectly or something related to the registry keys. Is there a way
to specify the C compiler path to f2py so that it does not rely on the
distutils? Or maybe I am totally off base here, I don't know.

What C and Fortran compilers are you trying to use? You can look at f2py's help
for flags that you can use to help control where the compilers get picked up,
but you can't avoid distutils.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
M

Mangabasi

Mangabasi said:
Thanks for your prompt response. I think I got a lot closer but no
cigar yet.
This is the output
C:\fortrandll>f2py -c -m sample sample.pyf sample.for
numpy_info:
FOUND:
define_macros = [('NUMERIC_VERSION', '"\\"24.2\\""')]
include_dirs = ['C:\\Python24\\include']
running build
running config_fc
running build_src
building extension "sample" sources
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv
creating c:\docume~1\fb\locals~1\temp\tmpcosvgv\src
f2py: sample.pyf
Reading fortran codes...
Reading file 'sample.pyf'
Post-processing...
Block: sample
Block: sample
Post-processing (stage 2)...
Building modules...
Building module "sample"...
Constructing wrapper function "sample"...
ierr1,ierr2,aout = sample(ain)
Wrote C/API module "sample" to file "c:\docume~1\fb
\locals~1\temp\tmpcos
vgv\src/samplemodule.c"
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src\fortranobject.c'
to sources
.
adding 'c:\docume~1\fb\locals~1\temp\tmpcosvgv\src' to include_dirs.
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.c -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
copying C:\python24\lib\site-packages\f2py2e\src\fortranobject.h -> c:
\docume~1\
fb\locals~1\temp\tmpcosvgv\src
running build_ext
No module named msvccompiler in scipy_distutils, trying from
distutils..
error: The .NET Framework SDK needs to be installed before building
extensions f
or Python.
I think I have a problem with distutils' msvccompiler.py. It may be
the MacroExpander in distutils guessing the visual studio path
incorrectly or something related to the registry keys. Is there a way
to specify the C compiler path to f2py so that it does not rely on the
distutils? Or maybe I am totally off base here, I don't know.

What C and Fortran compilers are you trying to use? You can look at f2py's help
for flags that you can use to help control where the compilers get picked up,
but you can't avoid distutils.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco- Hide quoted text -

- Show quoted text -

I am using Visual Studio 6.0 and Compaq Visual Fortran 6.6.
 
R

Robert Kern

Mangabasi said:
I am using Visual Studio 6.0 and Compaq Visual Fortran 6.6.

Ah. You can't use VS6 with that version of Python. I believe you need the .NET
SDK 2003.

You could also use gcc, but I'm not sure if that will work well with Compaq
Visual Fortran; you might have to use gfortran.

http://www.develer.com/oss/GccWinBinaries

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
M

Mangabasi

Ah. You can't use VS6 with that version of Python. I believe you need the .NET
SDK 2003.

You could also use gcc, but I'm not sure if that will work well with Compaq
Visual Fortran; you might have to use gfortran.

http://www.develer.com/oss/GccWinBinaries

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

Would Python 2.5 work with Visual Studio 6.6?
 
R

Robert Kern

Mangabasi said:
Would Python 2.5 work with Visual Studio 6.6?

No.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
M

Mangabasi

No.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

I will try the GCC then. It is a shame that I could not get calldll
to work. It was very simple to use. I think I am making a mistake
with the argument types but not sure.

Thanks for your help, it is greatly appreciated.
 
L

Lenard Lindstrom

Mangabasi said:
I will try the GCC then. It is a shame that I could not get calldll
to work. It was very simple to use. I think I am making a mistake
with the argument types but not sure.

Thanks for your help, it is greatly appreciated.

Did you try ctypes?
>>> from ctypes import *
>>> sample=cdll.sample.sample_
>>> sample.restype=None
>>> sample.argtypes=[POINTER(c_int), POINTER(c_int), POINTER(c_double), POINTER(c_double)]
>>> e1 = c_int(0)
>>> e2 = c_int(0)
>>> ain = (c_double*3)(2.0, 3.0, 4.0)
>>> aout = (c_double*4)()
>>> sample(e1, e2, ain, aout)
>>> aout[:] [6.0, 9.0, 8.0, 12.0]
>>> e1.value 0
>>> e2.value
0

I compile the SAMPLE example with mingw g77 3.4.5:

g77 -shared -o sample.dll sample.for

I had to take out the "INTENT(OUT)"s because g77 didn't like them. And
"SAMPLE" became "sample_" in the dll. Also note that argument passing to
Fortran subroutines is strictly pass-by-reference. Thus the ain pointer.

Lenard Lindstrom
 
M

Mangabasi

I will try the GCC then. It is a shame that I could not get calldll
to work. It was very simple to use. I think I am making a mistake
with the argument types but not sure.
Thanks for your help, it is greatly appreciated.

Did you try ctypes?
from ctypes import *
sample=cdll.sample.sample_
sample.restype=None
sample.argtypes=[POINTER(c_int), POINTER(c_int), POINTER(c_double), POINTER(c_double)]
e1 = c_int(0)
e2 = c_int(0)
ain = (c_double*3)(2.0, 3.0, 4.0)
aout = (c_double*4)()
sample(e1, e2, ain, aout)
aout[:] [6.0, 9.0, 8.0, 12.0]
e1.value 0
e2.value
0

I compile the SAMPLE example with mingw g77 3.4.5:

g77 -shared -o sample.dll sample.for

I had to take out the "INTENT(OUT)"s because g77 didn't like them. And
"SAMPLE" became "sample_" in the dll. Also note that argument passing to
Fortran subroutines is strictly pass-by-reference. Thus the ain pointer.

Lenard Lindstrom- Hide quoted text -

- Show quoted text -

Lenard,

Now I tried it as you suggested. I did not install G77 yet. I tried
it with the dll I already had. Something interesting happened:
from ctypes import *
sample=cdll.sample_dll.SAMPLE
sample.restype=None
sample.argtypes=[POINTER(c_int), POINTER(c_int), POINTER(c_double), POINTER(c_double)]
sample.argtypes=[POINTER(c_int), POINTER(c_int), POINTER(c_double), POINTER(c_double)]
e1 = c_int(-10)
e2 = c_int(-10)
ain = (c_double*3)(2.0, 3.0, 4.0)
ain = (c_double*3)(2.0, 3.0, 4.0)
aout = (c_double*4)()
aout = (c_double*4)()
sample(e1, e2, ain, aout)
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
ValueError: Procedure called with not enough arguments (16 bytes
missing) or wrong calling convention
[6.0, 9.0, 8.0, 12.0]

I got an error message and the expected answer! Any guesses?
 
M

Mangabasi

I will try the GCC then. It is a shame that I could not get calldll
to work. It was very simple to use. I think I am making a mistake
with the argument types but not sure.
Thanks for your help, it is greatly appreciated.

Did you try ctypes?
from ctypes import *
sample=cdll.sample.sample_
sample.restype=None
sample.argtypes=[POINTER(c_int), POINTER(c_int), POINTER(c_double), POINTER(c_double)]
e1 = c_int(0)
e2 = c_int(0)
ain = (c_double*3)(2.0, 3.0, 4.0)
aout = (c_double*4)()
sample(e1, e2, ain, aout)
aout[:] [6.0, 9.0, 8.0, 12.0]
e1.value 0
e2.value
0

I compile the SAMPLE example with mingw g77 3.4.5:

g77 -shared -o sample.dll sample.for

I had to take out the "INTENT(OUT)"s because g77 didn't like them. And
"SAMPLE" became "sample_" in the dll. Also note that argument passing to
Fortran subroutines is strictly pass-by-reference. Thus the ain pointer.

Lenard Lindstrom- Hide quoted text -

- Show quoted text -

A little bit of googling solved the problem. instead of

I used


and now it seems to be working without error messages.

Thanks a lot.
 
L

Lenard Lindstrom

Mangabasi said:
A little bit of googling solved the problem. instead of


I used



and now it seems to be working without error messages.

Thanks a lot.

I remember someone on the ctypes mailing list mentioning that g77 uses
the C calling convention for exported functions. Other compilers might
default to standard calls. At least with ctypes one can tinker with
calling convention and function arguments to make a call work.

Lenard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top