Vous êtes sur la page 1sur 44

A short introduction to F2PY

Pierre Schnizer November 10, 2002


Abstract For long times and even nowadays FORTRAN is the language of choice to implement CPU intensive tasks. F2PY offers capabilities to automatically wrap arbitrary FORTRAN routines. In this tutorial this process is illustrated by means of a simple example. Further points are described where f2py could be mislead.

1 Introduction
Since the introduction of FORTRAN many solvers have been implemented dealing with various numerical problems (See e.g. http://www.netlib.org). Even nowadays many projects are based on FORTRAN due to its excellent numerical capabilities. Analysing a typical problem one nds that 10% of the Code account for 90% of the time. Developing programs in interpreted languages was found to be faster than in compiled languages. Since Guido van Russum implemented Python many packages were added due to its extensibility. In 1999 Pearu Peterson started to develop a tool which allows to wrap FORTRAN Routines similar as SWIG does for C. f2py uses a mixed language approach, writing an C interface to convert the python data to C structures, and FORTRAN wrappers, when the C structures can not be passed to the FORTRAN routine directly. In this article I illustrate the necessary steps to wrap a FORTRAN routine by means of a simple routine followed by an callback illustration.

First steps

As rs example a sum implemented in FORTRAN is shown. So all this routine does is n b=


i=1

ai .

(1)

The corresponding code is found in the le simple/dsum.f.


1 2 3 subroutine dsum ( b , a , n ) double p r e c i s i o n a ( n ) , b b = 0 . 0 D0

4 5 6 7

100

do 1 0 0 i = 1 , n b = b + a( i ) continue end

f2py wraps this code so it can be accessed from python. > denotes the shell prompt in this document. The rst step is to ask f2py to write the signature of the routine to a description le. So in the directory simple I type at the shell prompt: >f2py -m dsum -h dsum.pyf dsum.f The -m ag gives the name the python module should have. The -h ag tells f2py in which le it should write the signature. After that all the FORTRAN les are listed f2py should parse. In the case here it is only the le dsum.f. Now the le dsum.pyf should look like the following lines. It uses the interface specications of FORTRAN 90 with some extensions.
1 2 3 4 5 6 7 8 9 10 11 12 13 !%f 9 0 f 9 0 python module dsum ! i n i n t e r f a c e ! i n : dsum subroutine dsum ( b , a , n ) ! i n : dsum : dsum . f double p r e c i s i o n : : b double p r e c i s i o n dimension ( n ) : : a i n t e g e r optional , check ( len ( a ) >=n ) , depend ( a ) : : n= len ( a ) end subroutine dsum end i n t e r f a c e end python module dsum ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 2 3 . 1 9 0 1 3 7 2 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

So everything after the exlamation mark is a comment. The rst line tricks editors in FORTAN 90 mode. The python module line denes the name of the module. Here the interface contains only one subroutine dsum. This subroutine takes an array of type double precision with length n and returns the sum of its elements. So one line 4 one can see the signature of the FORTRAN routine. Then b is declared. On line 6 the array and its dimension is declared. The declaration of n is unusual. Here one can see four non FORTRAN keywords: depend, check, len and =. Depend tells f2py which other variables are needed to fully resolve the value of the variable in question. So here also the variable a is needed. And it is used to calculate n using the size of the python array a. After the double colon you nd the cooresponding statement n=len(a). The f2py wrapper still allows one more trick. If n is given, it checks if a has at least equal elements as n says. The optional keyword is also known from FORTRAN. Here it means that the python wrapper can make n an optional argument. Now intent statements are added to dene b as output and a and n as input:
1 2 3 !%f 9 0 f 9 0 python module dsum ! i n i n t e r f a c e ! i n : dsum

4 5 6 7 8 9 10

subroutine dsum ( b , a , n ) ! i n : dsum : dsum . f double p r e c i s i o n , i n t e n t ( out ) : : b double p r e c i s i o n dimension ( n ) , i n t e n t ( in ) : : a i n t e g e r optional , check ( len ( a ) >=n ) , depend ( a ) , i n t e n t ( in ) : : n= len ( a ) end subroutine dsum end i n t e r f a c e end python module dsum

Now everything is ready and the module can be compiled. F2py will try to nd a compiler in your path and use it. So all to be typed is: >f2py -c dsum.pyf dsum.f f2py will write the wrapper les, compile dsum.f and the wrapper les, and link them in a shared object. After this step one can start python and load the extension: > python >>> import dsum >>> print dsum. doc This module dsum is auto-generated with f2py (version:2.23.190-1372). Functions: b = dsum(a,n=len(a)) . >>> print dsum.dsum. doc dsum - Function signature: b = dsum(a,[n]) Required arguments: a : input rank-1 array(d) with bounds (n) Optional arguments: n := len(a) input int Return objects: b : oat >>> print dsum.dsum((1,2,3,4,5)) 15.0 >>> print dsum.dsum((1,2,3,4,5), 2) 3.0 One can see that f2py also added some description to the wrapped functions. The last but one line shows the rst example and the last line illustrates the use of the optional argument n. Voill` a, the rst fortran extension is ready.

3 Wrapping a simple routine


Todays power supplys are often based on thyristors. When these devices cut the current, they can generate electromagnetic noise disturbing faint sensor signals (See also Figure 1). 3

2 1.5 1 UN [mV] 0.5 0

0.5 1 1.5 2 0 10 20 30 t [ms] 40 50 60

Figure 1: Electromagnetic noise generated by an power supply. The induced voltage UN in (Milli volts) is given versus time t in (Milliseconds). The main periodicity has a frequency of 600 Hz .

One Peak can be described as


N

UN (t) =
i=1

qi eri (tt0i ) sin [si (t t0i ) + i ]

t0 < t < t0 + max(q ) 5,

(2)

with UN the voltage of the noise and q, r, s, t0 , the coefcients of one vibration mode. The coefcients are generated randomly for each mode, and each peak is made up of N modes; N is typically selected to 10. As a deterministic evaluation of UN was needed for one run, 600 peaks were generated during setup. Further it was used inside a loop, so evaluation time became critical. Therefore I decided to implement the coefcient generation part in Python and the code for Equation (2) in FORTRAN (See also Section C). The FORTRAN function evaluates an vector containing m points in time:
1 subroutine expnta ( v o l t s , m, time , f r e q , n , ampl , phase , decay , t0 , tend )

volts correspond to UN , freq to s, ampl to q , phase to , decay to r. t0 and tend describe the time interval in which the formula shall be evaluated cutting away of many irrelevant components. Using the above routine I will show which steps are necessary to generate the wrapper: First start f2py to generate a signature le (assuming that the Fortran code is in le expnoise.f): 4

> f2py

-h ExponentNoise.pyf

-m

ExponentNoise expnoise.f

The option -h tells f2py to generate a signature le with name ExponentNoise.pyf for the routines found in expnoise.f. -m tells f2py the name of the target. The following le ExponentNoise.pyf is generated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 !%f 9 0 f 9 0 python module ExponentNoise ! i n interface ! in : ExponentNoise f u n c t i o n expnt ( time , f r e q , n , ampl , phase , decay , t0 , tend ) ! i n : ExponentNoise : ex pn oi se . f double p r e c i s i o n : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q ) >=n ) , depend ( f r e q ) : : n= len ( freq ) double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend double p r e c i s i o n : : expnt end f u n c t i o n expnt subroutine expnta ( v o l t s ,m, time , f r e q , n , ampl , phase , decay , t0 , tend ) ! in : ExponentNoise : e xp no i se . f double p r e c i s i o n dimension (m) : : v o l t s i n t e g e r optional , check ( len ( v o l t s ) >=m) , depend ( v o l t s ) : : m= len ( volts ) double p r e c i s i o n dimension (m) , depend (m) : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q ) >=n ) , depend ( f r e q ) : : n= len ( freq ) double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend end subroutine expnta end i n t e r f a c e end python module ExponentNoise ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 1 7 . 1 7 7 1 2 6 5 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

The syntax is borrowed from the FORTRAN 90 interfaces. The rst line says
python module ExponentNoise

stating the name of the python module. So in the compile run of f2py one will see, that f2py will generate a Python module with the name ExponentNoise. The next line starts the interface denition for that module:
interface ! in : ExponentNoise

On lines 4 14 the denition of the FORTRAN function expnt is given followed by the denition of the subroutine expnta on lines 15-26. Then the interface and the 5

module is closed. Now lets focus on the subroutine denition. First the parameters of the Fortran Routine are listed (Line 15) in the order of the Fortran function. In the Lines 16 26 the input data types are listed as in FORTRAN 90 interface blocks. Here some other additional tasks have to be fullled. Python array store their size, so the user does not need to pass their size explicit. The key tokens depend, = and check help the compile process to deal with this issue: = on line 18 tells f2py to calculate m using the size of the volts array. f2py will add m as an optional argument, allowing to specify a subset of the array. check tells f2py to check the variable properties. Here it states, that the volts array needs more or equal items than the value of m. depend tells f2py, that this variable needs more information. On line 20 it tells f2py, that n needs the variable freq. (f2py will check if the freq vector has the correct length). Now I start to change the le to reect my needs. f2py could not derive the intent of the variables, as there was nothing stated in the code. So I add a intent(in) statement to all variables except for the one describing the volts variable which gets an intent(out) statement, being the result of my calculation. (See also Listing 1). But in this constellation the intent(out) has a serious side effect. Normally f2py will generate an array of appropriate size for all intent(out) statements. But here the volts variable is used to calculate m. As the caller will not pass the array to the wrapper it can not calculate m. So the allocation of the volts array will fail. Therefore the = equal statement for m (Line 17) has to be changed to:
i n t e g e r , optional , check ( len ( time ) >=m) , depend ( time ) , i n t e n t ( in ) : : m= len ( time )

Also the depend(m) statement must be removed from line 18. (The time variable does not depend on m any more, as m is calculated form the shape of the time array). Now the wrapper will use the size of the time array to calculate m and to allocate the appropriate volts array. As I only want to wrap the subroutine, (the function is a helper function) I delete the lines 4 to 14 describing the function expnt. So the nal interface le is given in (Listing 1). Listing 1: Interface le ExponentNoise.pyf
1 2 3 4 5 6 7 8 9 !%f 9 0 f 9 0 python module ExponentNoise ! i n interface ! in : ExponentNoise subroutine expnta ( v o l t s ,m, time , f r e q , n , ampl , phase , decay , t0 , tend ) ! in : ExponentNoise : e xp no i se . f double p r e c i s i o n dimension (m) , i n t e n t ( in ) : : time i n t e g e r optional , check ( len ( time ) >=m) , depend ( time ) : : m= len ( time ) double p r e c i s i o n dimension (m) , depend (m) , i n t e n t ( out ) : : v o l t s double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q ) >=n ) , depend ( f r e q ) : : n= len ( freq )

10 11 12 13 14 15 16 17

double p r e c i s i o n dimension ( n ) double p r e c i s i o n dimension ( n ) double p r e c i s i o n dimension ( n ) double p r e c i s i o n dimension ( n ) double p r e c i s i o n dimension ( n ) end subroutine expnta end i n t e r f a c e end python module ExponentNoise

, depend ( n ) , depend ( n ) , depend ( n ) , depend ( n ) , depend ( n )

, , , , ,

i n t e n t ( in ) i n t e n t ( in ) i n t e n t ( in ) i n t e n t ( in ) i n t e n t ( in )

:: :: :: :: ::

ampl phase decay t0 tend

To compile the extension module ExponentNoise (.so or. . . . depending on your OS) one types: > f2py -c ExponentNoise.pyf expnoise.f in the shell. The -c option tells f2py

dll or

to write the wrapper based on the information found in the le ExponentNoise.pyf, to generate the appropriate interface code and to generate the extension. Following this .pyf-le all libraries and FORTRAN les must be listed, which contain the code of the extension. In this case its just one le expnoise.f. Now I start python, import the module1 and read its doc strings:
> python >>> import ExponentNoise >>> print ExponentNoise. doc This module ExponentNoise is auto-generated with f2py (version:2.17.177-1265). Functions: volts = expnta(time,freq,ampl,phase,decay,t0,tend,m=len(time),n=len(freq)) . >>> print ExponentNoise.expnta. doc expnta - Function signature: volts = expnta(time,freq,ampl,phase,decay,t0,tend,[m,n]) Required arguments: time : input rank-1 array(d) with bounds (m) freq : input rank-1 array(d) with bounds (n) ampl : input rank-1 array(d) with bounds (n) phase : input rank-1 array(d) with bounds (n)
If import fails with the following or a similar message: >>> import ExponentNoise Traceback (innermost last): File "<stdin>", line 1, in ? ImportError: ./ExponentNoise.so: undefined symbol: expnta you most likely forgot to add expnoise.f as an argument when compiling. For shared objects (or dlls) all routines can not be resolved at link time , so unresolved routines are only identied during import. If you add expnoise.f, you will see that f2py compiles this routine and links this routine to the ExponentNoise dynamic object.
1

decay : input rank-1 array(d) with bounds (n) t0 : input rank-1 array(d) with bounds (n) tend : input rank-1 array(d) with bounds (n) Optional arguments: m := len(time) input int n := len(freq) input int Return objects: volts : rank-1 array(d) with bounds (m)

The module doc string gives a short summary of the wrapped routines. Here its only the routine expnta. It requests the array time, whose subrange can be given optionally using m, and the arrays freq,ampl,phase, decay, t0, tend, whose subrange can be given by n. It is recommended to wrap the Fortran function in python routine, as it is simpler to add meaningful error messages in python. (f2py already gives nice error messages, but still one often needs more.) Further changes to the Fortran code, or the signature le can result in a reordering of arguments, so a python wrapper allows adaption only changing code in one place. The routine is wrapped by:
c l a s s E x p o n e n t N o i s e D i r e c t C a l l ( ExponentNoiseWrapper ) : The whole Formula i s c a l c u l a t e d i n F o r t r a n def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : r e t u r n expnta ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime )

All the data handling code is given in Listing 4. (Warning it handles also the following examples. So up to now only the direct test has been implemented .) The interface code is given in Listing 5 (Again, up to now, only the class ExponentNoiseDirectCall) is used. With this information given in this section you are already prepared to wrap standard Fortran routines. The following steps need to be done: 1. Use f2py to generate a signature le. 2. Edit this signature le to your needs 3. Compile the extension module. 4. Write a python wrapper for your comfort.

Pitfalls when passing arrays with more than one dimension

In the above example all arrays had only one dimension. The numerical extensions follow the C conventions, where the last index is the fastest, while the rst index is the fastest running for FORTRAN arrays. To familiarise lets develop the following simple routine, which takes an array, prints its elements, and multiplies it with 2. 8

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

subroutine ind2d ( a , m, n ) IMPLICIT NONE i n t e g e r n ,m double p r e c i s i o n a (m, n ) integer i , j

do j = 1 , n do i = 1 ,m w r i t e ( , ) a ( , i , , , j , ) = , a ( i , j ) a(i , j ) = a(i , j ) 2.0 enddo enddo end

The signature le is given by,after adding the intent statements:


1 2 3 4 5 6 7 8 9 10 !%f 9 0 f 9 0 python module I n d e x T e s t ! i n interface ! in : IndexTest subroutine ind2d ( a ,m, n ) ! i n : I n d e x T e s t : i n d e x t e s t . f double p r e c i s i o n dimension (m, n ) , i n t e n t ( in , out ) : : a i n t e g e r optional , check ( shape ( a , 0 ) ==m) , depend ( a ) , i n t e n t ( in ) : : m=shape ( a , 0 ) i n t e g e r optional , check ( shape ( a , 1 ) ==n ) , depend ( a ) , i n t e n t ( in ) : : n=shape ( a , 1 ) end subroutine ind2d end i n t e r f a c e end python module I n d e x T e s t

Here you can see the macro for checking the size of arrays with more than one dimension: shape. It works like the Numpy shape function, except that instead of writing shape(a)[0] one writes shape(a,0). Further notice, that f2py has changed the check to == as the return array has the same name as the one passed in, which means that the input array itself is used as return value. To test I add a simple test routine (see also Listing 2):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def t e s t ( ) : a = arange ( 6 ) a . shape = ( 2 , 3 ) b = a p r i n t I n d e x T e s t R e s u l t s print a print array ( a) print F o r t r a n Output b = I n d e x T e s t . ind2d ( a ) print Results print a print array ( a) print b print array (b) p r i n t

When you run the le you can see from the output2 that the same value is accessed with the same indices i and j. Only the numbers on the right are not ascending in order due to the different indexing of arrays in Python and Fortran. If you examined the above signature le carefully, you will have noticed, that I wrote
Some Fortran Compilers (e.g. Nag f95) send the output of write(*,*) to a le with a default name. (For Nag f95 it is fort.6 .)
2

double p r e c i s i o n dimension (m, n ) , i n t e n t ( in , out ) : : a

This is a important difference to intent(inout). As you know this intent statement is not valid for FORTRAN 90. There it would be intent(inout). f2py supports intent(inout), it is, however, better practice to change it to intent(in,out) because: the intent(in,out) statement tells f2py to copy the data of a into a new array, which is then changed by FORTRAN. Python arrays can possess many references. So a in place change of values can yield unexpected results. The f2py wrappers tries to address any conversion issues (e.g. casting the array to a proper type). This can result in the creation of some temporary array. To get the intent(inout) behaviour the data would need to be copied back again. As these double copying does not make sense f2py is not supporting it. Only in one case this statement makes sense: If the array is so big, that making a copy results in a serious performance impact on the target machine. The above mentioned oddities are shown by means of the le index test/IndexTestWRONG.pyf and a second test routine to index test/test.py. There you can see, that you allocate the array with proper type it have to transpose it to satisfy the (inout) requirements, resulting that one always has to take the fact into account that the indices in Python are reversed with respect to C. I.e a[i,j] = a(j,i). Sub arrays of a would unexpectedly. (Any reference to the array becomes a FORTRAN EQUIVALENCE statement). So if the FORTRAN routine changes the values of some array, declare it intent(in,out) unless the array needs very large amounts of memory. If you worry about performance compile the appropriate module with the option -DF2PY REPORT ATEXIT. For the ExponentNoise module the command would be: > f2py -c -DF2PY_REPORT_ATEXIT ExponentNoise.pyf expnoise.f

When the program exits, the time spent in the wrappers and the Fortran routine will be displayed. Listing 2: The python test routine
# ! / u s r / b i n / env p y t h o n import I n d e x T e s t import IndexTestWRONG from Numeric import def p r i n t a r r a y ( a ) : f o r j in range ( a . shape [ 1 ] ) : f o r i in range ( a . shape [ 0 ] ) : print [ , i , , , j , ] = , a [ i , j ] def t e s t ( ) : a = arange ( 6 )

10

a = reshape ( a , ( 3 , 2 ) ) b = a p r i n t I n d e x T e s t R e s u l t s print a print array ( a) print F o r t r a n Output b = I n d e x T e s t . ind2d ( a ) print Results print a print array ( a) print b print array (b) p r i n t

def testWRONG ( ) : a = arange ( 6 ) . a s t y p e ( F l o a t ) a = reshape ( a , ( 3 , 2 ) ) b = a p r i n t I n d e x T e s t WRONG R e s u l t s print a print array ( a) print F o r t r a n Output # A needs t o be t r a n s p o s e d t o be a proper a r r a y # for fortran a = transpose ( a ) i f not IndexTestWRONG . h a s c o l u m n m a j o r s t o r a g e ( a ) : p r i n t A i s not i n proper s t o r a g e ! p r i n t Can not pass such an a r r a y ! IndexTestWRONG . ind2d ( a ) print Results print a print array ( a) print b print array (b) p r i n t if name = = main : test () testWRONG ( )

5 Wrapping a call back


Up to now only FORTRAN routines were discussed, which only call FORTRAN routines. Many solvers however evaluate a user dened routine (e.g. ordinary differential equation solvers need a routine describing the differential equation system), which can be implemented in python. f2py provides this capabilities.

5.1 A simple python function


The rst example is very simple. Instead of calling the Fortran function sin in Equation (2) a call back is used. (I know that it does not make much sense form a computing point of view.) The changes to the Fortran les are given in Section D. The rst step: > f2py -h ExponentNoiseCallback.pyf expnoise_callback.f writes a signature le. The le is listed here: 11

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

!%f 9 0 f 9 0 python module e x p n t f u s e r r o u t i n e s interface expntf user interface subroutine df ( tmp1 , tmp2 ) ! i n e x p n o i s e c a l l b a c k . f : e x p n t f : u n k n o w n i n t e r f a c e double p r e c i s i o n : : tmp1 double p r e c i s i o n : : tmp2 end subroutine df end i n t e r f a c e e x p n t f u s e r i n t e r f a c e end python module e x p n t f u s e r r o u t i n e s python module e x p n t a f u s e r r o u t i n e s interface expntaf user interface e x t e r n a l df end i n t e r f a c e e x p n t a f u s e r i n t e r f a c e end python module e x p n t a f u s e r r o u t i n e s f u n c t i o n e x p n t f ( time , f r e q , n , ampl , phase , decay , t0 , tend , df ) ! i n e x p n o i s e c a l l b a c k . f use e x p n t f u s e r r o u t i n e s double p r e c i s i o n : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q )>=n ) , depend ( f r e q ) : : n= len ( f r e q ) double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend e x t e r n a l df double p r e c i s i o n : : e x p n t f end f u n c t i o n e x p n t f subroutine e x p n t a f ( v o l t s ,m, time , f r e q , n , ampl , phase , decay , t0 , tend , df ) ! i n e x p n o i s e c a l l b a c k . f use e x p n t a f u s e r r o u t i n e s double p r e c i s i o n dimension (m) : : v o l t s i n t e g e r optional , check ( len ( v o l t s )>=m) , depend ( v o l t s ) : : m= len ( v o l t s ) double p r e c i s i o n dimension (m) , depend (m) : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q )>=n ) , depend ( f r e q ) : : n= len ( f r e q ) double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend e x t e r n a l df end subroutine e x p n t a f ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 1 7 . 1 7 7 1 2 6 5 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

Here the most notable part are the user modules. When f2py nds a routine in the argument list, it will try to guess its signature and generates an appropriate module wrapping the callback. Here you can see, that it found the subroutine df called by the expntf function (Line 4). For subroutine expntaf this mechanism failed, as the callback is not evaluated there, but passed to expntf. Further notice, that f2py added a use statement to the function and subroutine denitions (Line 16 and 29) to import the appropriate modules. Now I add the intent statements, and copy the denition of the subroutine df from the expnt user routines to the expnta user routines. I delete the statements concerning the expntf function, as my entry point is the expnta routine. So I nally get the le:
1 2 3 4 5 !%f 9 0 f 9 0 python module e x p n t f u s e r r o u t i n e s interface expntf user interface subroutine df ( tmp1 , tmp2 ) ! i n : E x p o n e n t N o i s e C a l l b a c k : e x p n o i s e c a l l b a c k . f : e x p n t f : unknown interface double p r e c i s i o n , i n t e n t ( in ) : : tmp1

12

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

double p r e c i s i o n , i n t e n t ( out ) : : tmp2 end subroutine df end i n t e r f a c e e x p n t f u s e r i n t e r f a c e end python module e x p n t f u s e r r o u t i n e s python module ExponentNoiseCallback ! i n interface ! in : ExponentNoiseCallback subroutine e x p n t a f ( v o l t s ,m, time , f r e q , n , ampl , phase , decay , t0 , tend , df ) ! i n : ExponentNoiseCallback : e x p n o i s e c a l l b a c k . f use e x p n t f u s e r r o u t i n e s double p r e c i s i o n dimension (m) : : v o l t s i n t e g e r optional , check ( len ( v o l t s )>=m) , depend ( v o l t s ) : : m= len ( v o l t s ) double p r e c i s i o n dimension (m) , depend (m) : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q )>=n ) , depend ( f r e q ) : : n= len ( f r e q ) double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend e x t e r n a l df end subroutine e x p n t a f end i n t e r f a c e end python module ExponentNoiseCallback

For testing I subclass the ExponentNoiseWrapper.


1 2 3 4 5 6 7 8 9 10 11 c l a s s ExponentNoiseCallBackSimple ( ExponentNoiseWrapper ) : C a l l b a c k I l l u s t r a t i o n . Use t h e s i n from Numeric i n s t e a d o f t h e F o r t r a n one . def s i n ( s e l f , angle ) : r e t u r n s i n ( angle ) def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : v o l t s = e x p n t a f ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime , lambda x , y= s e l f : y . s i n ( x ) ) return vo lts

The lambda function is necessary, as f2py accepts only pure functions or lambdas as a call back routine.

5.2 Calling the whole formula


So up to now you are ready to generate a Call back for nearly any Fortran 77 code. Here I want to show you a second issue, advanced creation of help arrays. Complex Fortran 77 routines often need work arrays. Here I will show how an array of varying size can be easily specied to f2py. In Section D the full code is listed for the function ndf. Here I add all value of time for one pair of coefcients i. Python is called to evaluate Equation (2). As the data is stored in arrays the python evaluation is very fast. The signature of the routine ndf is given by:
subroutine f i n d f ( v o l t s , m, time , f r e q , n , ampl , phase , 1 decay , t0 , tend , pyfunc , help1 , help2 , l )

Here two help arrays have to be passed with size l. f2py writes the following wrapper:

13

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

subroutine f i n d f ( v o l t s ,m, time , f r e q , n , ampl , phase , decay , t0 expnoise callback . f use f i n d f u s e r r o u t i n e s double p r e c i s i o n dimension (m) : : v o l t s i n t e g e r optional , check ( len ( v o l t s )>=m) , depend ( v o l t s ) double p r e c i s i o n dimension (m) , depend (m) : : time double p r e c i s i o n dimension ( n ) : : f r e q i n t e g e r optional , check ( len ( f r e q )>=n ) , depend ( f r e q ) : : double p r e c i s i o n dimension ( n ) , depend ( n ) : : ampl double p r e c i s i o n dimension ( n ) , depend ( n ) : : phase double p r e c i s i o n dimension ( n ) , depend ( n ) : : decay double p r e c i s i o n dimension ( n ) , depend ( n ) : : t 0 double p r e c i s i o n dimension ( n ) , depend ( n ) : : tend e x t e r n a l pyfunc double p r e c i s i o n dimension ( l ) : : help1 double p r e c i s i o n dimension ( l ) , depend ( l ) : : help2 i n t e g e r optional , check ( len ( help1 )>=l ) , depend ( help1 ) end subroutine f i n d f

, tend , pyfunc , help1 , help2 , l ) ! i n

: : m= len ( v o l t s )

n= len ( f r e q )

: : l = len ( help1 )

In the last lines the help arrays are listed again. The function can deal with arbitrary size arrays, so I changed the last lines to:
1 2 3 i n t e g e r , i n t e n t ( in ) : : l double p r e c i s i o n dimension ( l ) , depend ( l ) , o p t i o n a l : : help1 double p r e c i s i o n dimension ( l ) , depend ( l ) , o p t i o n a l : : help2

Now the help arrays got optional arguments, and l will yield the size. In the wrapper, you can see that l has been set to 1000.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 c l a s s ExponentNoiseCallBack1d ( ExponentNoiseWrapper ) : Handles one data s e t o f f r e q , ampl , phase , decay f o r a long data sample o f time . def c a l l ( s e l f , help1 , f r e q , ampl , phase , decay ) : r e t u r n exp ( help1 decay ) ampl s i n ( help1 f r e q + phase ) def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : l = 1000 v o t l s = f i n d f ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime , lambda x , y , z , a , b , c , s= s e l f : s . call (x ,y , z , a ,b) , l) return vo lts

f2py will now allocate the memory under the hood, and passes the arrays to the called function.

6 A real world example


All examples above were centered around a simple example. Here a full wrapper for the lsodare routine from the ODEPACK (see is shown. As a rst step a simpler wrapper using hidden work arrays is shown. As a second step an advanced wrapper providing a class hiding all internal details will be descirbed. In this section the following features of f2py will be described: Callbacks intent(hide, copy, cache) 14

MAX

6.1

A simple wrapper

As described in section 3 the raw f2py le is generated. A full listing of this le is given in Listing 6. Due to its size only the most important parts are shown here: The call back wrapper. When f2py nds a function in the argument list of the routine it tries to write such a description le. The user routines denes a module which describes call back functions. Here one can see that it found three such items. But only the function is called in the routine lsodar and not the others. Thus, it can only generate the interface for only for the function f and not for the others.
1 2 3 4 5 6 7 8 9 10 11 12 13 !%f 9 0 f 9 0 python module l s o d a r u s e r r o u t i n e s interface lsodar user interface external jac external g subroutine f ( neq , t , y , e r w o r k l f 0 e ) ! i n : l s o d a r : l s o d a r . f : l s o d a r : u n k n o w n i n t e r f a c e i n t e g e r dimension ( 1 ) : : neq double p r e c i s i o n : : t double p r e c i s i o n dimension ( 1 ) : : y double p r e c i s i o n : : e r w o r k l f 0 e end subroutine f end i n t e r f a c e l s o d a r u s e r i n t e r f a c e end python module l s o d a r u s e r r o u t i n e s

In the lsodar module the use statement includes the description of the call backs.
14 15 16 17 18 python module l s o d a r ! i n interface ! in : l s o d a r subroutine l s o d a r ( f , neq , y , t , to ut , i t o l , r t o l , a t o l , i t a s k , i s t a t e , i o p t , rwork , lrw , iwork , liw , j a c , j t , g , ng , j r o o t ) ! i n : l s o d a r : l s o d a r . f use l s o d a r u s e r r o u t i n e s external f

The last variable in the routine signatur is jroot. The following variables are not part of the routine signature but belong to the common block of the routine. Here only a part is shown.
37 38 39 40 i n t e g e r dimension ( ng ) : : j r o o t double p r e c i s i o n dimension ( 2 0 9 ) : : rowns double p r e c i s i o n : : ccmax double p r e c i s i o n : : e l 0

And here the declaration of the block itself follows.


104 105 i n t e g e r : : mxords common / l s 0 0 0 1 / rowns , ccmax , e l 0 , h , hmin , hmxi , hu , rc , tn , uround , i l l i n , i n i t , lyh , lewt , l a c o r , l s a v f , lwm, liwm , mxstep , mxhnil , nhnil , ntrep , n s l a s t , nyh , iowns , i c f , i e r p j , i e r s l , j c u r , j s t a r t , k f l a g , l , meth , miter , maxord , maxcor , msbp , mxncf , n , nq , nst , nfe , n j e , nqu common / l s r 0 0 1 / rownr3 , t0 , t l a s t , t o u t c , lg0 , lg1 , lgx , iownr3 , i r f n d , i t a s k c , ngc , nge common / l s a 0 0 1 / tsw , rowns2 , pdnorm , i n s u f r , i n s u f i , ix pr , iowns2 , j t y p , mused , mxordn , mxords end subroutine l s o d a r end i n t e r f a c e end python module l s o d a r ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 1 3 . 1 7 5 1 2 5 0 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

106 107 108 109 110 111 112 113

15

To generate the additional callback routines (the jacobi and the border) one writes a help le with the approbriate functions and let f2py wrap these functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 subroutine f e x ( neq , t , y , ydot ) double p r e c i s i o n t , y , ydot dimension y ( neq ) , ydot ( neq ) end subroutine gex ( neq , t , y , ng , gout ) double p r e c i s i o n t , y , gout dimension y ( neq ) , gout ( ng ) end subroutine j a c ( neq , t , y , ml , mu, pd , nrowpd ) i n t e g e r neq , ml , mu double p r e c i s i o n t , y ( neq ) , pd ( nrowpd , neq ) end

Now this function is wrapped and the descriptions are copied into the lsodar user routines. The result is shown in Listing 7. Now the le is ready, but does not reect the needs. Here I will show two nal interfaces to lsodar. One is kept as simple as possible, and the other will show how to wrap a FORTRAN function in a class.

6.2

A simple interface

The following interface should be as straight forward as possible and only exhibit the necessary details. So again intent(in) and intent(out) were added to all lines. Two work arrays rwork and iwork have to be provided to lsodar. These are generated using the intent(hide, cache) statement. hide tells f2py not to add them as optional arguments to the list. And cache tells f2py to generate them once during loading of the module and keep them in memory.
44 45 double p r e c i s i o n dimension ( 2 2 + neq MAX( 1 6 , neq +9) + 3 ng ) , i n t e n t ( hide , cache ) : : rwork i n t e g e r dimension (20+ neq ) , i n t e n t ( hide , cache ) : : iwork

A new statement is MAX. It allows here to calculate the necessary size of the work arrays. It is always good practise to introduce a python layer between the wrapped module and the rest of the code. The following wrapper allows to specify a border if desired and calculates the size of the array returned by the border automatically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # ! / u s r / b i n / env p y t h o n import Numeric import l s o d a r s i m p l e def l s o d a r ( f , y , t , tout , args , keywords ) : Wrapper around l s o d a r from ODEPACK i t o l = 1 ; r t o l = 1 e 6; a t o l = 1 e 6 task = 1 ; s t a t e = 1 ; j a c = None ; jt = 2 i f keywords . has key ( border ) : border = keywords [ border ] tmp = border ( t , y )

16

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

i f type ( tmp ) ! = type ( Numeric . a r r a y ( ( 0 , 0 ) ) ) : r a i s e TypeError , The border f u n c t i o n must r e t u r n an a r r a y ! ng = l e n ( tmp ) a s s e r t ( ng ! = None ) a s s e r t ( ng > 0 ) else : border = lambda x : x ng = 0 i f keywords . has key ( j a c o b i ) : j a c o b i = keywords [ j a c o b i ] else : j a c o b i = lambda x : x a s s e r t ( i t o l = = 1 or i t o l = = 2 ) y , t , s t a t e , j r o o t = l s o d a r s i m p l e . l s o d a r ( f , y , t , tout , i t o l , r t o l , a t o l , task , s t a t e , j a c o b i , j t , border , ng ) return y , t , state , j r o o t

6.3 Data handling in the class


The above wrapper does not use the full functionality of lsodar. lsodar can save the status of the calculation internally and is faster if one continoues with the calculation. This state is stored in COMMON blocks. lsodar provides functions so that the data of the COMMON blocks can be set from arrays and stored in arrays. The class illustrated here will handle these data internally. Further lsodar provides functions to calculate the derivatives at each calculational step. The class can offer a method offering this capabillity transparent to the user. Further optional parameters are stored in the work arrays. Methods with meaningful names can be probided.

6.4 The interface le


The full le is given in Listing 9. Two more functions were added. srcar copies the common block data and intdy allows to calculate the derivative.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 python module l s o d a r ! i n interface ! in : l s o d a r subroutine s r c a r ( rsav , i s a v , j o b ) double p r e c i s i o n , dimension ( 2 4 5 ) , check ( len ( r s a v ) > =245) : : r s a v i n t e g e r , dimension ( 5 9 ) , check ( len ( i s a v ) > =59) : : i s a v integer : : job end subroutine s r c a r subroutine i n t d y ( t , deriv , rwork , nyh , dky , i f l a g ) double p r e c i s i o n , i n t e n t ( in ) : : t i n t e g e r , i n t e n t ( in ) : : d e r i v double p r e c i s i o n dimension ( : ) , i n t e n t ( inout ) : : rwork i n t e g e r , i n t e n t ( in ) , r e q u i r e d : : nyh double p r e c i s i o n , dimension ( nyh ) , i n t e n t ( out ) : : dky i n t e g e r i n t e n t ( out ) : : i f l a g end subroutine i n t d y

Lsodar allows to specify the calculational tolerances for each part of the differential equation system or one for all. iopt=1 is one parameter for all and iopt=2 is the second case. Using MAX() f2py can check that.
78 double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) , depend ( i t o l ) : : rtol

17

79

double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) , depend ( i t o l ) : : atol

Above the work arrays were hidden. Here it is desired to store them inside the instance so that each instance keeps track about its status.
83 84 double p r e c i s i o n dimension ( : ) , check ( len ( rwork ) > =(22 + neq MAX( 1 6 , neq +9) + 3 ng ) ) , depend ( neq ) , depend ( ng ) , i n t e n t ( inout ) : : rwork i n t e g e r dimension ( : ) , check ( len ( iwork ) >=(20+neq ) ) , depend ( neq ) , i n t e n t ( inout ) : : iwork

The whole class is not listed here, only the main call to the wrapper.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 y = self .y t0 = s e l f . t s e l f . p r a e ( ) # W r i t e Common B l o c k s tmp = l s o d a r . l s o d a r ( integrand , y , t0 , t , # Type o f t o l e r a n c e s and t o l e r a n c e s s e l f . itol , s e l f . rtol , s e l f . atol , # T a s k and s t a t u s o f c a l c u l a t i o n task , s e l f . s t a t e , # O p t i o n a l p a r a m t e r s and work a r r a y s i o p t , s e l f . rwork , s e l f . iwork , # J a c o b i matrix , e x t e r n a l not supported y e t jacobi , jt , # Border c o n d i t i o n s border , s e l f . borderdimension ) yout , t , s t a t e , j r o o t = tmp s e l f . p o s t ( ) # S a v e Common B l o c k S t a t u s s e l f . y = yout self . t = t

6.5 What could be added to the Interface


Jacobi Matrix Support varying the number of equations . . . Rewrite part of the call method in FORTRAN.

7 Using Advanced Features of FORTRAN 90


The FORTRAN 90 standard brought a new set of features. Here I will show how to handle two new features: the kind operator and arrays with implicit size. Dynamically allocated arrays are not discussed here.

7.1

Calling FORTRAN functions with sized arrays

FORTRAN 90 allows to pass arrays without specifying their size using separate parameters. All arrays can be SIZEed. So the size of array a dened as
double p r e c i s i o n dimension ( : , : ) :: a

can be determined by calling: 18

d1 = SIZE ( a , 1 ) d2 = SIZE ( a , 2 )

with d1,d2 making the dimension available in the routine. However the storage of the length dimension is implementation dependent. But one can write a FORTRAN wrapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 subroutine wrap foo ( a , d1 , d2 , b , d3 ) IMPLICIT NONE i n t e g e r , i n t e n t ( in ) i n t e g e r , i n t e n t ( out ) double p r e c i s i o n , dimension ( d1 , d2 ) , i n t e n t ( in ) double p r e c i s i o n , dimension ( d3 ) , i n t e n t ( out )

:: :: :: ::

d1 , d2 d3 a b

INTERFACE SUBROUTINE FOO ( a , b ) IMPLICIT NONE double p r e c i s i o n , dimension ( : , : ) , i n t e n t ( in ) : : a double p r e c i s i o n , dimension ( : ) , i n t e n t ( out ) : : b END SUBROUTINE FOO END INTERFACE CALL foo ( a , b ) d3 = SIZE ( b ) end subroutine wrap foo

Here the arrays are rst declared with a xed dimension (Lines 5 and 6). During run time these dimensions are then xed to the array. Then in the interface for the subroutine foo the arrays are declared as FORTRAN 90 style arrays. In the subroutine foo the array a can be sized.

7.2 The kind operator


The kind operator allows to inform the compiler what accuracy is needed for the variables. The compiler then chooses the appropriate native type. When writing this document f2py did not support this feature. Still routines using such type declarations can be wrapped. Find out the type the kind operator evaluates to e.g. by making a small FORTRAN stand alone program and printing the return value of the kind call. Use the documentation of your FORTRAN compiler, to nd out to which C-type it maps. Generate the f2py le as described before. Now edit the le, remove the kind entry and replace the integer or oat with the integer* or oat* with the appropriate byte count.

Adding a new compiler

Even if f2py supports an impressive list of compilers, still one will nd compilers not supported yet. A full description would exceed the limit of this tutorial, so I 19

focus here on the main steps to add a new compiler. As an example I use the Lahey - Compiler. As a rst step I write a small FORTRAN program:
1 a = 2 w r i t e ( , ) s i n ( a ) end

After compilation the executable is used to search for the shared libraries, which this object needs. Tools like ldd can list the necessary libraries. On my machine running linux ldd displays: libfj9i6.so.1 => /opt/lf9560/lib/libfj9i6.so.1 (0x40023000) libfj9f6.so.1 => /opt/lf9560/lib/libfj9f6.so.1 (0x400a7000) libc.so.6 => /lib/libc.so.6 (0x40191000) libm.so.6 => /lib/libm.so.6 (0x40286000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) Here the interesting two lines are the rst two, which point to the libraries libfj9i6.so.1 and libfj9f6.so.1 . These libraries are needed during the linking of the python extension. All the Fortran compiler issues are encapsulated by the class fortran compiler base in the le scipy distutils/command/build ib.py. For each supported compiler a subclass is derived. Most of the time it is simpler to use a full implemented compiler subclass and not to start from the beginning. So I chose the class nag fortran compiler as a starting point. In Listing 3 the implementation of the class is shown. Listing 3: The class describing the lahey compiler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 c lass lahey fortran compiler ( fortran compiler base ) : vendor = Lahey/ F u j i t s u ver match = r Lahey/ F u j i t s u F o r t r a n 9 5 Compiler R e l e a s e ( ? P< v e r s i o n > [ \ s ] ) def i n i t ( s e l f , f c = None , f 9 0 c = None ) : fortran compiler base . init ( self ) i f f c i s None : fc = lf95 i f f 9 0 c i s None : f90c = fc s e l f . f77 compiler = fc s e l f . f90 compiler = f90c switches = debug = g chk c h k g l o b a l s e l f . f77 switches = s e l f . f90 switches = switches s e l f . f77 switches = s e l f . f77 switches + fix s e l f . f77 debug = s e l f . f90 debug = debug

20

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

s e l f . f77 opt = s e l f . f90 opt = s e l f . get opt ( ) s e l f . ver cmd = s e l f . f 7 7 c o m p i l e r + v e r s i o n try : d i r = os . environ [ LAHEY ] s e l f . l i b r a r y d i r s = [ os . path . j o i n ( d i r , l i b ) ] e x c e p t KeyError : self . library dirs = [] s e l f . l i b r a r i e s = [ fj9f6 , fj9i6 , fj9ipp , fj9e6 ]

def g e t o p t ( s e l f ) : opt = O r e t u r n opt def g e t l i n k e r s o ( s e l f ) : r e t u r n [ s e l f . f 7 7 c o m p i l e r , shared ]

. To make the new class available to the build process one has to add the compiler to the le build ib.py and add it to the list of available compilers at the bottom of this le.

21

B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

The data handling Code


Listing 4: The data handling ExponentNoise.py

# ! / u s r / b i n / env p y t h o n import Numeric import MLab import f 7 7

def checkargumentshape ( args , names ) : t e s t s h a p e = a r g s [ 0 ] . shape passed = 1 f o r i in a r g s [ 1 : ] : i f t e s t s h a p e ! = i . shape : passed = 0 break i f passed ! = 1 : errm = The shape o f a l l + s t r ( l e n ( a r g s ) ) + \ arguments must be t h e same ! \ n f o r i in range ( l e n ( a r g s ) ) : errm = errm + Argument + s t r ( i ) + + names [ i ] + \ had a shape o f + s t r ( a r g s [ i ] . shape ) + \ n r a i s e TypeError , errm

c l a s s FortranExponentNoise : def i n i t ( s e l f , f r e q , ampl , phase , decay , t0 , type ) : checkargumentshape ( ( f r e q , ampl , phase , decay , t 0 ) , ( Frequency , Amplitude , Phase , Decay , t 0 ) ) s e l f . starttime = t0 tmp = Numeric . a b s o l u t e ( 1 . / decay ) 5 0 . 0 s e l f . maxdecay = MLab . max ( tmp ) s e l f . endtime = t 0 + tmp s e l f . freq = freq s e l f . amplitude = ampl s e l f . phase = phase s e l f . decay = decay s e l f . S e t F o r t r a n F u n c t i o n ( type ) def S e t F o r t r a n F u n c t i o n ( s e l f , type ) : s e l f . e x p f o r t r a n = f 7 7 . ExponentNoise ( type ) def e v a l ( s e l f , time ) : v o l t s = s e l f . e x p f o r t r a n . e v a l ( time , s e l f . f r e q , s e l f . amplitude , s e l f . phase , s e l f . decay , s e l f . s t a r t t i m e , s e l f . endtime ) return vo lts

def t e s t E x p o n e n t N o i s e ( ) : from RandomArray import uniform import Gnuplot len = 1000 n = 10 maxvolts = 1 g = Gnuplot . Gnuplot ( ) myfreq myampl mydecay myt0 = = = = uniform ( l e n 5 0 , len 100 , uniform( maxvolts , maxvolts , uniform ( 3. len , 1. len , uniform ( 0 . 0 , 1e 3 , ( n len ( n len ( n len ( n len ,) ,) ,) ,) ) ) ) )

22

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

tmp tmp myt0 phase tmp

= = = = =

Numeric . arange ( l e n ) 2 Numeric . p i / l e n Numeric . r a v e l ( Numeric . m u l t i p l y . o u t e r ( Numeric . ones ( n ) , tmp ) ) tmp + myt0 Numeric . z e r o s ( ( l e n n ) ) MLab . max ( Numeric . a b s o l u t e ( 1 . / mydecay ) ) 5 . 0

data = [ ] step = 0 f o r type in ( d i r e c t , simplecb , cb1d , cb2d ) : # b f o r type in ( cb2d , ) : print Constructing Objects t = FortranExponentNoise ( myfreq , myampl , phase , mydecay , myt0 , type ) print Evaluation hlp = Numeric . arange ( 3 l e n ) 2 Numeric . p i / l e n / 5 0 0 . r e s = t . e v a l ( hlp ) data . append ( Gnuplot . Data ( hlp , r e s +step , with= l i n e , t i t l e =type ) ) step = step + 2 apply ( g . p l o t , data ) raw input ( ) def t e s t ( ) : testExponentNoise ( ) if name test () == main :

Listing 5: The data handling f77/ init .py


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # ! / u s r / b i n / env p y t h o n Encapsulates the Fortran 7 7 Versions of the t e s t s from Numeric import s i n , exp , ones , multiply , sum , F l o a t , r a v e l , where , l e s s from Numeric import t r a n s p o s e from MLab import min , max from ExponentNoise import expnta from ExponentNoiseCallback import expntaf , f i n d f , f i n d f 2 d

c l a s s ExponentNoiseWrapper : instance counter = 0 def init ( self ) : s e l f . instance counter = s e l f . instance counter + 1 def e v a l ( v o l t s , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : Pure V i r t u a l c l a s s , use a derived one f o r t h e implementation ! c l a s s E x p o n e n t N o i s e D i r e c t C a l l ( ExponentNoiseWrapper ) : The whole Forumla i s c a l c u l a t e d i n F o r t r a n def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : r e t u r n expnta ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) c l a s s ExponentNoiseCallBackSimple ( ExponentNoiseWrapper ) : C a l l b a c k I l l u s t r a t i o n . Use t h e s i n from Numeric i n s t e a d o f t h e f o r t r a n one . def s i n ( s e l f , angle ) : r e t u r n s i n ( angle ) def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) :

23

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

v o l t s = e x p n t a f ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime , lambda x , y= s e l f : y . s i n ( x ) ) return vo lts c l a s s ExponentNoiseCallBack1d ( ExponentNoiseWrapper ) : Handles one data s e t o f f r e q , ampl , phase , decay f o r a long data sample o f time . def c a l l ( s e l f , help1 , f r e q , ampl , phase , decay ) : r e t u r n exp ( help1 decay ) ampl s i n ( help1 f r e q + phase ) def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : v o l t s = ones ( time . shape , F l o a t ) f i n d f ( v o l t s , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime , lambda x , y , z , a , b , c , s= s e l f : s . call (x ,y , z , a ,b) , 1000) return vo lts def e x p o n e n t n o i s e c l b 2 d ( help1 , f r e q , ampl , phase , decay , n1 , j , l ) : help1 [ : n1 , : ] = exp ( help1 [ : n1 , : ] decay [ : n1 , : ] ) ampl [ : n1 , : ] s i n ( help1 [ : n1 , : ] f r e q [ : n1 , : ] + phase [ : n1 , : ] ) r e t u r n help1

c l a s s ExponentNoiseCallBack2d ( ExponentNoiseWrapper ) : Gathers d a t a s e t s o f f r e q , ampl , phase , decay f o r some time v a l u e s . This a l l o w s t o e v a l u a t e t h e s p a r s e v a l u e s as a matrix i n python . def c a l l ( s e l f , help1 , f r e q , ampl , phase , decay , n1 ) : tmp = m u l t i p l y . o u t e r ( ones ( n1 ) , help1 ) tmp1 = exp ( tmp decay [ : n1 , : ] ) ampl [ : n1 , : ] tmp2 = s i n ( tmp [ : n1 , : ] f r e q [ : n1 , : ] + phase [ : n1 , : ] ) r e t u r n sum ( tmp1 tmp2 , 0 ) def e v a l ( s e l f , time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime ) : check , v o l t s = f i n d f 2 d ( time , f r e q , amplitude , phase , decay , s t a r t t i m e , endtime , e x p o n e n t n o i s e c l b 2 d , 100 , 100) i f check < 0 : p r i n t Got t o column : , check r a i s e ValueError , Not enough data f o r s e e n f o r each column p r i n t Used , check , number o f columns ! return vo lts

expevaltypedict = { direct : ExponentNoiseDirectCall , simplecb : ExponentNoiseCallBackSimple , cb1d : ExponentNoiseCallBack1d , cb2d : ExponentNoiseCallBack2d } def ExponentNoise ( type ) : try : tmp = e x p e v a l t y p e d i c t [ type ] e x c e p t KeyError , des : errm = The s p e c i f i e d type + s t r ( type ) + i s unknown . + \ I only know about t h e f o l l o w i n g keys : + \ s t r i n g . j o i n ( e x p e v a l t y p e d i c t . keys ( ) , ) r a i s e TypeError , errm r e t u r n tmp ( )

if

name test ()

==

main

24

C The FORTRAN source expnoise.f


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 C C C a l c u l a t e s t h e e x p o n e n t i a l v o l t a g e f o r a given time t C f u n c t i o n expnt ( time , f r e q , n , ampl , phase , decay , t0 , tend ) IMPLICIT NONE Integer n double p r e c i s i o n expnt , time , f r e q ( n ) , ampl ( n ) , phase ( n ) , 1 decay ( n ) , t 0 ( n ) , tend ( n )

Integer i , t e s t c double p r e c i s i o n alpha , mydec , c a l c t , tmp1 expnt = 0 . 0 D0 testc = 0 DO 4 0 0 , i = 1 , n i f ( time . g t . t 0 ( i ) . and . time . l t . tend ( i ) ) then c a l c t = time t 0 ( i ) tmp1 = c a l c t f r e q ( i ) alpha = ampl ( i ) dsin ( tmp1 + phase ( i ) ) mydec = alpha dexp ( c a l c t decay ( i ) ) expnt = expnt + mydec testc = testc + 1 endif 4 0 0 CONTINUE END C C C a l c u l a t e s expnt f o r each time ( i ) C subroutine expnta ( v o l t s , m, time , f r e q , n , ampl , phase , 1 decay , t0 , tend ) IMPLICIT NONE I n t e g e r m, n double p r e c i s i o n expnt , time (m) , f r e q ( n ) , ampl ( n ) , phase ( n ) , 1 decay ( n ) , t 0 ( n ) , tend ( n ) , v o l t s (m)

Integer i DO 5 0 0 , i = 1 ,m v o l t s ( i ) = expnt ( time ( i ) , f r e q , n , ampl , phase , decay , 1 t0 , tend ) 5 0 0 CONTINUE END

D The FORTRAN source expnoise callback.f


1 2 3 4 5 6 7 8 9 10 11 12 13 C C Author : P i e r r e S c h n i z e r < P i e r r e . Schnizer@cern . ch> C Date : 2 2 . May 2 0 0 2 . C Purpose : T u t o r i a l f o r f2py C L i c e n s e : LGPL . You should have r e c i e v e d a copy o f t h e l i c e n s e t o g e t h e r with C t h i s s o f t w a r e . No Warrenty a t a l l . C C C C a l c u l a t e s t h e e x p o n e n t i a l v o l t a g e f o r a given time t a c c o r d i n g t o t h e C formula : C v o l t = exp ( decay ( t t 0 ) ) ampl df ( ( t t o ) f r e q phase ) f o r C all t 0 < time < tend C

25

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

f u n c t i o n e x p n t f ( time , f r e q , n , ampl , phase , decay , t0 , tend , df ) IMPLICIT NONE Integer n e x t e r n a l df double p r e c i s i o n expntf , time , f r e q ( n ) , ampl ( n ) , phase ( n ) , 1 decay ( n ) , t 0 ( n ) , tend ( n )

Integer i double p r e c i s i o n alpha , mydec , c a l c t , tmp1 , tmp2 e x p n t f = 0 . 0 D0 DO 4 0 0 , i = 1 , n i f ( time . g t . t 0 ( i ) . and . time . l t . tend ( i ) ) then c a l c t = time t 0 ( i ) tmp1 = c a l c t f r e q ( i ) + phase ( i ) tmp2 = 10 CALL df ( tmp1 , tmp2 ) ! tmp2 = d s i n ( tmp1 ) alpha = ampl ( i ) tmp2 mydec = alpha dexp ( c a l c t decay ( i ) ) e x p n t f = e x p n t f + mydec endif CONTINUE END

400

C C C a l c u l a t e s expnt f o r each time ( i ) C subroutine e x p n t a f ( v o l t s , m, time , f r e q , n , ampl , phase , 1 decay , t0 , tend , df ) IMPLICIT NONE I n t e g e r m, n e x t e r n a l df double p r e c i s i o n expntf , time (m) , f r e q ( n ) , ampl ( n ) , phase ( n ) , 1 decay ( n ) , t 0 ( n ) , tend ( n ) , v o l t s (m)

Integer i w r i t e ( , ) n ,m = , n , m DO 5 0 0 , i = 1 ,m C write ( , ) i = , i v o l t s ( i ) = e x p n t f ( time ( i ) , f r e q , n , ampl , phase , decay , 1 t0 , tend , df ) 5 0 0 CONTINUE END C C C Only Search f o r t h e i n t e r v a l s , where exp ( decay t ) s i n ( a t + phi ) C has t o be e v a l u a t e d . C a l l Python t o e v a l u a t e t h e formula . In t h i s c a s e i t i s C done d e a l i n g with a l l as many data from t h e time v e c t o r as p o s s i b l e using one C ( f r e q , ampl , phase , decay ) data s e t . The c a l l i n g r o u t i n e must a l l o c a t e t h e C h e l p e r a r r a y s t o some s i z e ( l ) ( Only v a l u e s < = m make s e n s e . ) This a l l o w s f o r C memory tuning o f t h e c a l l b a c k . C subroutine f i n d f ( v o l t s , m, time , f r e q , n , ampl , phase , 1 decay , t0 , tend , pyfunc , help1 , help2 , l ) IMPLICIT NONE I n t e g e r m, n , l e x t e r n a l pyfunc double p r e c i s i o n time (m) , f r e q ( n ) , ampl ( n ) , phase ( n ) , 1 decay ( n ) , t 0 ( n ) , tend ( n ) , v o l t s (m) , 2 help1 ( l ) , help2 ( l )

I n t e g e r i , j , k , l 1 , ind

26

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

C C

w r i t e ( , ) n ,m = , n , m, l I n i t a l i s e v o l t s t o zero : DO 5 0 0 , k = 1 ,m v o l t s ( k ) = 0 . 0 D0 5 0 0 CONTINUE DO 7 0 0 , k = 1 , n j = 0 DO 6 0 0 , i = 1 , m w r i t e ( , ) T e s t i n g : , i , time ( i ) , t 0 ( k ) , tend ( k ) i f ( time ( i ) . g t . t 0 ( k ) . and . time ( i ) . l t . tend ( k ) ) then j = j + 1 w r i t e ( , ) Adding time : , time ( i ) , a t index , j help1 ( j ) = time ( i ) t 0 ( k ) help2 ( j ) = i endif i f ( j . ge . l ) then Help a r r a y i s f i l l e d C a l l python f u n c t i o n f o r a l l r e l e v a n t data w r i t e ( , ) i n do c a l l i n g pyf with , l , arguments CALL pyfunc ( help1 , l , f r e q ( k ) , ampl ( k ) , phase ( k ) , 2 decay ( k ) ) DO 5 5 0 , l 1 = 1 , l ind = help2 ( l 1 ) w r i t e ( , ) adding v o l t s , help1 ( l 1 ) , a t index , ind v o l t s ( ind ) = v o l t s ( ind ) + help1 ( l 1 ) CONTINUE j = 0 endif CONTINUE C a l l t h e pyton f u n c t i o n f o r t h e r e s t o f t h e help a r r a y i f ( j . g t . 0 ) then w r i t e ( , ) f i n i s h i n g c a l l i n g pyf with , j , arguments CALL pyfunc ( help1 , j , f r e q ( k ) , ampl ( k ) , phase ( k ) , 2 decay ( k ) ) DO 6 5 0 , l 1 = 1 , j ind = help2 ( l 1 ) w r i t e ( , ) adding v o l t s , help1 ( l 1 ) , a t index , ind v o l t s ( ind ) = v o l t s ( ind ) + help1 ( l 1 ) CONTINUE endif CONTINUE END

C C C

C 550

600 C C

C 650 700

27

E The lsodar wrapper code


Listing 6: lsodar/lsodar raw.pyf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 !%f 9 0 f 9 0 python module l s o d a r u s e r r o u t i n e s interface lsodar user interface external jac external g subroutine f ( neq , t , y , e r w o r k l f 0 e ) ! i n : l s o d a r : l s o d a r . f : l s o d a r : u n k n o w n i n t e r f a c e i n t e g e r dimension ( 1 ) : : neq double p r e c i s i o n : : t double p r e c i s i o n dimension ( 1 ) : : y double p r e c i s i o n : : e r w o r k l f 0 e end subroutine f end i n t e r f a c e l s o d a r u s e r i n t e r f a c e end python module l s o d a r u s e r r o u t i n e s python module l s o d a r ! i n interface ! in : l s o d a r subroutine l s o d a r ( f , neq , y , t , to ut , i t o l , r t o l , a t o l , i t a s k , i s t a t e , i o p t , rwork , lrw , iwork , liw , j a c , j t , g , ng , j r o o t ) ! i n : l s o d a r : l s o d a r . f use l s o d a r u s e r r o u t i n e s external f i n t e g e r dimension ( 1 ) : : neq double p r e c i s i o n dimension ( 1 ) : : y double p r e c i s i o n : : t double p r e c i s i o n : : t o u t integer : : i t o l double p r e c i s i o n dimension ( 1 ) : : r t o l double p r e c i s i o n dimension ( 1 ) : : a t o l integer : : itask integer : : i s t a t e integer : : iopt double p r e c i s i o n dimension ( lrw ) : : rwork i n t e g e r optional , check ( len ( rwork )>=lrw ) , depend ( rwork ) : : lrw= len ( rwork ) i n t e g e r dimension ( liw ) : : iwork i n t e g e r optional , check ( len ( iwork )>=liw ) , depend ( iwork ) : : liw= len ( iwork ) external jac integer : : j t external g i n t e g e r optional , check ( len ( j r o o t )>=ng ) , depend ( j r o o t ) : : ng= len ( j r o o t ) i n t e g e r dimension ( ng ) : : j r o o t double p r e c i s i o n dimension ( 2 0 9 ) : : rowns double p r e c i s i o n : : ccmax double p r e c i s i o n : : e l 0 double p r e c i s i o n : : h double p r e c i s i o n : : hmin double p r e c i s i o n : : hmxi double p r e c i s i o n : : hu double p r e c i s i o n : : r c double p r e c i s i o n : : tn double p r e c i s i o n : : uround integer : : i l l i n integer : : i n i t i n t e g e r : : lyh i n t e g e r : : lewt integer : : lacor integer : : lsavf i n t e g e r : : lwm i n t e g e r : : liwm i n t e g e r : : mxstep i n t e g e r : : mxhnil integer : : nhnil integer : : ntrep integer : : nslast i n t e g e r : : nyh i n t e g e r dimension ( 6 ) : : iowns

28

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

106 107 108 109 110 111 112 113

integer : : i c f integer : : i e r p j integer : : i e r s l integer : : jcur integer : : j s t a r t integer : : kflag integer : : l i n t e g e r : : meth integer : : miter i n t e g e r : : maxord i n t e g e r : : maxcor i n t e g e r : : msbp i n t e g e r : : mxncf integer : : n i n t e g e r : : nq integer : : nst integer : : nfe integer : : nje i n t e g e r : : nqu double p r e c i s i o n dimension ( 2 ) : : rownr3 double p r e c i s i o n : : t 0 double p r e c i s i o n : : t l a s t double p r e c i s i o n : : t o u t c integer : : lg0 integer : : lg1 integer : : lgx i n t e g e r dimension ( 2 ) : : iownr3 integer : : irfnd integer : : itaskc i n t e g e r : : ngc i n t e g e r : : nge double p r e c i s i o n : : tsw double p r e c i s i o n dimension ( 2 0 ) : : rowns2 double p r e c i s i o n : : pdnorm integer : : insufr integer : : insufi integer : : ixpr i n t e g e r dimension ( 2 ) : : iowns2 integer : : jtyp i n t e g e r : : mused i n t e g e r : : mxordn i n t e g e r : : mxords common / l s 0 0 0 1 / rowns , ccmax , e l 0 , h , hmin , hmxi , hu , rc , tn , uround , i l l i n , i n i t , lyh , lewt , l a c o r , l s a v f , lwm, liwm , mxstep , mxhnil , nhnil , ntrep , n s l a s t , nyh , iowns , i c f , i e r p j , i e r s l , j c u r , j s t a r t , k f l a g , l , meth , miter , maxord , maxcor , msbp , mxncf , n , nq , nst , nfe , n j e , nqu common / l s r 0 0 1 / rownr3 , t0 , t l a s t , t o u t c , lg0 , lg1 , lgx , iownr3 , i r f n d , i t a s k c , ngc , nge common / l s a 0 0 1 / tsw , rowns2 , pdnorm , i n s u f r , i n s u f i , ix pr , iowns2 , j t y p , mused , mxordn , mxords end subroutine l s o d a r end i n t e r f a c e end python module l s o d a r ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 1 3 . 1 7 5 1 2 5 0 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

Listing 7: lsodar/lsodarsimple.pyf
1 2 3 4 5 6 7 8 9 !%f 9 0 f 9 0 python module l s o d a r u s e r r o u t i n e s interface lsodar user interface subroutine i n t e g r a n d ( neq , t , y , ydot ) i n t e g e r optional , check ( len ( y )>=neq ) , depend ( y ) , i n t e n t ( in ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) , i n t e n t ( in ) : : y double p r e c i s i o n dimension ( neq ) , depend ( neq ) , i n t e n t ( out ) , check ( len ( ydot )>=neq ) : : ydot end subroutine i n t e g r a n d

29

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

subroutine border ( neq , t , y , ng , gout ) i n t e g e r optional , check ( len ( y )>=neq ) , depend ( y ) , i n t e n t ( in ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) , i n t e n t ( in ) : : y i n t e g e r optional , check ( len ( gout ) ==ng ) , depend ( gout ) , i n t e n t ( hide ) : : ng= len ( gout ) double p r e c i s i o n dimension ( ng ) , i n t e n t ( out ) : : gout end subroutine border subroutine j a c o b i ( neq , t , y , ml , mu, pd , nrowpd ) i n t e g e r optional , i n t e n t ( in ) , check ( len ( y )>=neq ) , depend ( y ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) : : y i n t e g e r , i n t e n t ( in ) : : ml i n t e g e r , i n t e n t ( in ) : : mu double p r e c i s i o n dimension ( nrowpd , neq ) , depend ( neq ) : : pd i n t e g e r i n t e n t ( in ) , optional , check ( shape ( pd , 0 ) ==nrowpd ) , depend ( pd ) : : nrowpd= shape ( pd , 0 ) end subroutine j a c o b i end i n t e r f a c e l s o d a r u s e r i n t e r f a c e end python module l s o d a r u s e r r o u t i n e s python module l s o d a r s i m p l e ! i n interface ! in : l s o d a r subroutine l s o d a r ( integrand , neq , y , t , tou t , i t o l , r t o l , a t o l , i t a s k , i s t a t e , i o p t , rwork , lrw , iwork , liw , j a c o b i , j t , border , ng , j r o o t ) use l s o d a r u s e r r o u t i n e s external integrand i n t e g e r : : neq double p r e c i s i o n dimension ( neq ) , i n t e n t ( in , out ) : : y double p r e c i s i o n , i n t e n t ( in , out ) : : t double p r e c i s i o n , i n t e n t ( in ) : : t o u t i n t e g e r , i n t e n t ( in ) : : i t o l ! I f i t o l i s o n e s c a l a r s a r e p a s s e d n o t a r r a y s . Using MAX t o i n f o r m f 2 p y double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) : : r t o l double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) : : a t o l i n t e g e r , i n t e n t ( in ) : : i t a s k i n t e g e r , i n t e n t ( in , out ) : : i s t a t e i n t e g e r , i n t e n t ( in ) , parameter : : i o p t =1 double p r e c i s i o n dimension ( 2 2 + neq MAX( 1 6 , neq +9) + 3 ng ) , i n t e n t ( hide , cache ) : : rwork i n t e g e r dimension (20+ neq ) , i n t e n t ( hide , cache ) : : iwork i n t e g e r depend ( rwork ) , i n t e n t ( hide ) : : lrw= len ( rwork ) i n t e g e r depend ( iwork ) , i n t e n t ( hide ) : : liw= len ( iwork ) external jacobi i n t e g e r , i n t e n t ( in ) : : j t i n t e g e r , required , i n t e n t ( in ) : : ng i n t e g e r dimension (MAX( ng , 1 ) ) , i n t e n t ( out ) : : j r o o t e x t e r n a l border end subroutine l s o d a r end i n t e r f a c e end python module l s o d a r s i m p l e

Listing 8: lsodar/lsodarsimple.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # ! / u s r / b i n / env p y t h o n import Numeric import l s o d a r s i m p l e def l s o d a r ( f , y , t , tout , args , keywords ) : Wrapper around l s o d a r from ODEPACK i t o l = 1 ; r t o l = 1 e 6; a t o l = 1 e 6 task = 1 ; s t a t e = 1 ; j a c = None ; jt = 2 i f keywords . has key ( border ) : border = keywords [ border ] tmp = border ( t , y )

30

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

i f type ( tmp ) ! = type ( Numeric . a r r a y ( ( 0 , 0 ) ) ) : r a i s e TypeError , The border f u n c t i o n must r e t u r n an a r r a y ! ng = l e n ( tmp ) a s s e r t ( ng ! = None ) a s s e r t ( ng > 0 ) else : border = lambda x : x ng = 0 i f keywords . has key ( j a c o b i ) : j a c o b i = keywords [ j a c o b i ] else : j a c o b i = lambda x : x a s s e r t ( i t o l = = 1 or i t o l = = 2 ) y , t , s t a t e , j r o o t = l s o d a r s i m p l e . l s o d a r ( f , y , t , tout , i t o l , r t o l , a t o l , task , s t a t e , j a c o b i , j t , border , ng ) return y , t , state , j r o o t

E.1

Wrapping it in a class
Listing 9: lsodar/lsodar.pyf

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

!%f 9 0 f 9 0 python module l s o d a r u s e r r o u t i n e s interface lsodar user interface subroutine i n t e g r a n d ( neq , t , y , ydot ) i n t e g e r optional , check ( len ( y )>=neq ) , depend ( y ) , i n t e n t ( in ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) , i n t e n t ( in ) : : y double p r e c i s i o n dimension ( neq ) , depend ( neq ) , i n t e n t ( out ) , check ( len ( ydot )>=neq ) : : ydot end subroutine i n t e g r a n d subroutine border ( neq , t , y , ng , gout ) i n t e g e r optional , check ( len ( y )>=neq ) , depend ( y ) , i n t e n t ( in ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) , i n t e n t ( in ) : : y i n t e g e r optional , check ( len ( gout ) ==ng ) , depend ( gout ) , i n t e n t ( hide ) : : ng= len ( gout ) double p r e c i s i o n dimension ( ng ) , i n t e n t ( out ) : : gout end subroutine border subroutine j a c o b i ( neq , t , y , ml , mu, pd , nrowpd ) i n t e g e r optional , i n t e n t ( in ) , check ( len ( y )>=neq ) , depend ( y ) : : neq= len ( y ) double p r e c i s i o n , i n t e n t ( in ) : : t double p r e c i s i o n dimension ( neq ) : : y i n t e g e r , i n t e n t ( in ) : : ml i n t e g e r , i n t e n t ( in ) : : mu double p r e c i s i o n dimension ( nrowpd , neq ) , depend ( neq ) : : pd i n t e g e r i n t e n t ( in ) , optional , check ( shape ( pd , 0 ) ==nrowpd ) , depend ( pd ) : : nrowpd= shape ( pd , 0 ) end subroutine j a c o b i end i n t e r f a c e l s o d a r u s e r i n t e r f a c e end python module l s o d a r u s e r r o u t i n e s python module l s o d a r ! i n interface ! in : l s o d a r subroutine s r c a r ( rsav , i s a v , j o b ) double p r e c i s i o n , dimension ( 2 4 5 ) , check ( len ( r s a v ) > =245) : : r s a v i n t e g e r , dimension ( 5 9 ) , check ( len ( i s a v ) > =59) : : i s a v integer : : job end subroutine s r c a r subroutine i n t d y ( t , deriv , rwork , nyh , dky , i f l a g ) double p r e c i s i o n , i n t e n t ( in ) : : t i n t e g e r , i n t e n t ( in ) : : d e r i v double p r e c i s i o n dimension ( : ) , i n t e n t ( inout ) : : rwork

31

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

i n t e g e r , i n t e n t ( in ) , r e q u i r e d : : nyh double p r e c i s i o n , dimension ( nyh ) , i n t e n t ( out ) : : dky i n t e g e r i n t e n t ( out ) : : i f l a g end subroutine i n t d y subroutine lsodar wrap ( integrand , neq , y0 , yout , t , n2 , i t o l , r t o l , a t o l , i t a s k , i s t a t e , i o p t , rwork , lrw , iwork , liw , j a c o b i , j t , border , ng , j r o o t , l a s t ) use l s o d a r u s e r r o u t i n e s external integrand i n t e g e r optional , check ( len ( y0 )>=neq ) , depend ( y0 ) , i n t e n t ( in ) : : neq= len ( y0 ) double p r e c i s i o n dimension ( neq ) , i n t e n t ( in ) : : y0 double p r e c i s i o n dimension ( neq , n2 1 ) , depend ( neq ) , depend ( n2 ) , i n t e n t ( out ) : : yout double p r e c i s i o n dimension ( n2 ) : : t i n t e g e r optional , check ( len ( t )>=n2 ) , depend ( t ) : : n2= len ( t ) i n t e g e r , i n t e n t ( in ) : : i t o l ! I f i t o l i s o n e s c a l a r s a r e p a s s e d n o t a r r a y s . Using MAX t o i n f o r m f 2 p y double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) : : r t o l double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) : : a t o l i n t e g e r , i n t e n t ( in ) : : i t a s k i n t e g e r , i n t e n t ( in , out ) : : i s t a t e i n t e g e r , i n t e n t ( in ) : : i o p t double p r e c i s i o n dimension ( 2 2 + neq MAX( 1 6 , neq +9) + 3 ng ) , i n t e n t ( inout ) : : rwork i n t e g e r optional , check ( len ( rwork )>=lrw ) , depend ( rwork ) : : lrw= len ( rwork ) i n t e g e r dimension ( liw ) : : iwork i n t e g e r optional , check ( len ( iwork )>=liw ) , depend ( iwork ) , i n t e n t ( inout ) : : liw= len ( iwork ) external jacobi i n t e g e r , i n t e n t ( in ) : : j t e x t e r n a l border i n t e g e r , required , i n t e n t ( in ) : : ng i n t e g e r dimension (MAX( ng , 1 ) ) , i n t e n t ( out ) : : j r o o t i n t e g e r , i n t e n t ( out ) : : l a s t end subroutine lsodar wrap subroutine l s o d a r ( integrand , neq , y , t , tou t , i t o l , r t o l , a t o l , i t a s k , i s t a t e , i o p t , rwork , lrw , iwork , liw , j a c o b i , j t , border , ng , j r o o t ) use l s o d a r u s e r r o u t i n e s external integrand i n t e g e r , depend ( y ) , i n t e n t ( hide ) : : neq= len ( y ) double p r e c i s i o n dimension ( : ) , i n t e n t ( in , copy , out ) : : y double p r e c i s i o n , i n t e n t ( in , copy , out ) : : t double p r e c i s i o n , i n t e n t ( in ) : : t o u t i n t e g e r , i n t e n t ( in ) : : i t o l ! I f i t o l i s o n e s c a l a r s a r e p a s s e d n o t a r r a y s . Using MAX t o i n f o r m f 2 p y double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) , depend ( i t o l ) : : rtol double p r e c i s i o n dimension (MAX( neq ( i t o l 1) , 1 ) ) , i n t e n t ( in ) , depend ( i t o l ) : : atol i n t e g e r , i n t e n t ( in ) : : i t a s k i n t e g e r , i n t e n t ( in , out ) : : i s t a t e i n t e g e r , i n t e n t ( in ) : : i o p t double p r e c i s i o n dimension ( : ) , check ( len ( rwork ) > =(22 + neq MAX( 1 6 , neq +9) + 3 ng ) ) , depend ( neq ) , depend ( ng ) , i n t e n t ( inout ) : : rwork i n t e g e r dimension ( : ) , check ( len ( iwork ) >=(20+neq ) ) , depend ( neq ) , i n t e n t ( inout ) : : iwork i n t e g e r depend ( rwork ) , i n t e n t ( hide ) : : lrw= len ( rwork ) i n t e g e r depend ( iwork ) , i n t e n t ( hide ) : : liw= len ( iwork ) external jacobi i n t e g e r , i n t e n t ( in ) : : j t i n t e g e r , required , i n t e n t ( in ) : : ng i n t e g e r dimension (MAX( ng , 1 ) ) , i n t e n t ( out ) : : j r o o t e x t e r n a l border end subroutine l s o d a r end i n t e r f a c e end python module l s o d a r ! T h i s f i l e was a u t o g e n e r a t e d w i t h f 2 p y ( v e r s i o n : 2 . 1 3 . 1 7 5 1 2 5 0 ) . ! See http : / / cens . i o c . ee / p r o j e c t s / f2py2e /

32

Listing 10: lsodar/ClassIntegrate.py


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 # ! / u s r / b i n / env p y t h o n # # Author : P i e r r e S c h n i z e r < P i e r r e . S c h n i z e r @ c e r n . ch > # Date : 05. 11. 2002 # P u r p o s e : To i l l u s t r a t e t h e u s a g e and c a p a b i l i t y o f f 2 p y # V e r s i o n : Not e v e n a l p h a . T h i s i s i l l u s t r a t i o n c o d e , s o do n o t u s e i t i n # production environement # L i c e n s e : LGPL # import Numeric import MLab import l s o d a r class LastJob : j o b i d = 2 j o b l i s t = {} def G e t L a s t J o b I d ( s e l f ) : return s e l f . jobid def GetJob ( s e l f , id ) : r e t u r n s e l f . j o b l i s t [ id ] def R e g i s t e r ( s e l f , i n s t a n c e ) : tmp = id ( i n s t a n c e ) s e l f . j o b l i s t [ tmp ] = i n s t a n c e r e t u r n tmp def D e R e g i s t e r ( s e l f , i n s t a n c e ) : tmp = id ( i n s t a n c e ) del s e l f . j o b l i s t [ tmp ] def S e t A c t i v e J o b I d ( s e l f , id ) : s e l f . j o b i d = id lastjob = LastJob ( )

msgs = { 2 : I n t e g r a t i o n s u c c e s s f u l . , 3 : Found Border , 1: Ex ces s work done on t h i s c a l l ( perhaps wrong Dfun type ) . , 2: Ex ces s a c c u r a c y r e q u e s t e d ( t o l e r a n c e s too s m a l l ) . , 3: I l l e g a l i nput d e t e c t e d ( i n t e r n a l e r r o r ) . , 4: Repeated e r r o r t e s t f a i l u r e s ( i n t e r n a l e r r o r ) . , 5: Repeated convergence f a i l u r e s ( perhaps bad J a c o b i a n or +\ tolerances ) . , 6: E r r o r weight became zero during problem . , 7: I n t e r n a l workspace i n s u f f i c i e n t t o f i n i s h ( i n t e r n a l e r r o r ) . }

# C l a s s I n t e g r a t e class Integrate : Wraps l s o d a r . Allows m u l t i p l e i n s t a n c e s . LSODAR from t h e ODEPACK package provides a f l e x i b l e ODE s o l v e r . I t s o l v e s a system o f f i r s t order d i f f e r e n t i a l e q u a t i o n s . F u r t h e r i t s e a r c h e s f o r t h e r o o t s i n a f u n c t i o n s p e c i f i e d as border . For each problem you should s e t up a s e p e r a t e o b j e c t . Between c a l l s t h e i n t e r n a l s t a t e i s preserved . C u r r e n t l y j a c o b i a n m a t r i c e s can not be su p p lie d . def del ( self ) : s e l f . l a s t j o b . Deregister ( s e l f )

33

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

def

i n i t ( s e l f , integrand , Input : i n t e g r a n d . . . t h e y0 . . . the t0 . . . the o p t i o n a l : border

y0 , t0 , d i c ) : f u n c t i o n system d e s c r i b i n g t h e problem s t a r t v e c t o r f o r t h e dependent v a r i a b l e s s t a r t value f o r t h e independen v a r i a b l e

. . . a f u n t i o n r e t u r n i n g an a r r a y . When one o f i t s v a l u e s i s zero , t h e e v a l u a t i o n w i l l t e r m i n a t e .

self . lastjob = lastjob s e l f . jobid = s e l f . l a s t j o b . Register ( s e l f ) a s s e r t ( id ( s e l f ) = = id ( s e l f . l a s t j o b . GetJob ( s e l f . j o b i d ) ) ) border = None a r g s = None b o r d e r a r g s = None y0 = y0 t0 = t0 i f d i c . has key ( border ) : border = d i c [ border ] i f d i c . has key ( a r g s ) : args = tuple ( dic [ args ] ) i f d i c . has key ( b o r d e r a r g s ) : borderargs = tuple ( dic [ borderargs ] ) s e l f . args = args s e l f . borderargs = borderargs tmp = ( t0 , y0 ) i f a r g s ! = None : tmp + = a r g s dimension = l e n ( Numeric . a r r a y ( apply ( integrand , tmp ) ) ) i f border ! = None : tmp = ( t0 , y0 ) i f b o r d e r a r g s ! = None : tmp + = b o r d e r a r g s s e l f . borderdimension = l e n ( Numeric . a r r a y ( apply ( border , tmp ) ) ) s e l f . border = border else : s e l f . borderdimension = 0 s e l f . border = lambda x : x s e l f . integrand = integrand s e l f . r s a v = Numeric . z e r o s ( ( 2 4 5 ) , Numeric . F l o a t ) s e l f . i s a v = Numeric . z e r o s ( ( 5 9 ) , Numeric . I n t ) r l e n g t h = 2 2 + dimension MLab . max ( ( 1 6 , dimension +9) ) r l e n g t h + = 3 s e l f . borderdimension i l e n g t h = 2 0 + dimension s e l f . rwork = Numeric . z e r o s ( ( r l e n g t h ) , Numeric . F l o a t ) s e l f . iwork = Numeric . z e r o s ( ( i l e n g t h ) , Numeric . I n t ) self self self self self self self self . dimension = dimension . state = 1 . itol = 1 . r t o l = Numeric . a r r a y ( 1 e 6) . a t o l = Numeric . a r r a y ( 1 e 6) . iopt = 0 . y = y0 . t = t0

# E n a b l e Common B l o c k h a n d l i n g o f l s o d a r . f s e l f . needpostcleanup = 1 # print id of l a s t j o b : , id ( s e l f . l a s t j o b ) , # p r i n t P r i n t e d from i n i t : , i d ( s e l f ) def S e t P r e c i s i o n ( s e l f , which , p r e c i s i o n ) : tmp = Numeric . a r r a y ( p r e c i s i o n ) l e n g t h = l e n ( tmp ) a s s e r t ( l e n g t h = = 1 or l e n g t h = = s e l f . dimension )

34

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

i f which = = a b s o l u t e : s e l f . a t o l = tmp i f which = = r e l a t i v e : s e l f . r t o l = tmp al = len ( s e l f . a t o l ) r l = len ( s e l f . r t o l ) i f l e n g t h > 1 or a l > 1 or r l > 1 : self . itol = 2 else : self . itol = 1 if self . itol == 2: # Multidimensional . . . if al == 1: s e l f . a t o l = s e l f . a t o l Numeric . ones ( s e l f . dimension ) if il == 1: s e l f . i t o l = s e l f . i t o l Numeric . ones ( s e l f . dimension )

def S e t A b s o l u t e P r e c i s i o n ( s e l f , p r e c i s i o n ) : s e l f . SetPrecision ( absolute , precision )

def S e t R e l a t i v e P r e c i s i o n ( s e l f , p r e c i s i o n ) : se l f . SetPrecision ( relative , precision )

def R e i n i t ( s e l f , y0 , t 0 ) : self . state = 1 a s s e r t ( l e n ( y0 ) = = s e l f . dimension ) s e l f . y = y0 s e l f . t = t0 def c a l l ( self , t , dic ) : The c a l c u l a t i o n r o u t i n e . Input : t ... v a l u e s o f t h e independent v a r i a b l e . The dependent v a r i a b l e w i l l be c a l u c l a t e for a l l values of t [ 1 : ] So t h e l e n g t h o f t must be 2 a t minimum

Keywords :

t a s k : an index s p e c i f y i n g t h e t a s k t o be performed . t a s k has t h e f o l l o w i n g v a l u e s and meanings . 1 means normal computation o f output v a l u e s o f y ( t ) a t t = t o u t ( by o v e r s h o o t i n g and i n t e r p o l a t i n g ) . 2 means t a k e one s t e p only and r e t u r n . 3 means s to p a t t h e f i r s t i n t e r n a l mesh p o i n t a t or beyond t = t o u t and r e t u r n . I f you s p e c i f i y a c r i t i c a l p o i n t i n e v a l u a t i o n only value 1 and 2 a r e allowed . t c r i t : D e f i n e s a c r i t i c a l point , where t h e problem should not be e v a l u t e d . t c r i t may be equal t o or beyond t h e l a s t value o f t , but not behind i t i n t h e d i r e c t i o n o f i n t e g r a t i o n . t h i s o p t i o n i s u s e f u l i f t h e problem has a s i n g u l a r i t y a t or beyond t = t c r i t .

Output : yout , t , j r o o t yout ... t h e v a l u e s corresponding t o t t ... passed t o s e e what value t had i f a border was found jroot ... i n d i c a t e s which o f t h e border c o n d i t i o n s has been f u l f i l l e d . The l a s t v a l u e s o f t an

35

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

!!!!!!!!!!!!!!!!!!!! !!! Warning !!!! !!!!!!!!!!!!!!!!!!!! Save s t h e system s t a t e i n t e r n a l l y . So use one o b j e c t f o r each problem ! task = 1 i f d i c . has key ( t a s k ) : task = dic [ task ] i f d i c . has key ( t c r i t ) : i f t a s k ! = 1 and t a s k ! = 2 : des = Wrong Value o f t a s k when c a l c u l a t i n g with a c r i t c a l +\ time . Task was + s t r ( t a s k ) + but only 1 or 2 a r e allowed ! r a i s e ValueError , des task = task + 3 s e l f . rwork [ 1 1 ] = d i c [ t c r i t ] else : i f t a s k ! = 1 and t a s k ! = 2 and t a s k ! = 3 : des = Wrong Value o f t a s k when c a l c u l a t i n g with a c r i t c a l +\ time . Task was + s t r ( t a s k ) + but only 1 , 2 or +\ 3 a r e allowed ! r a i s e ValueError , des iopt = s e l f . iopt # C al l b a ck to j a c o b i not supported yet ! j a c o b i = lambda x : x jt = 2 # Handle a d d i t i o n a l a r g u m e n t s t o t h e f u n c t i o n s integrand = s e l f . integrand border = s e l f . border i f s e l f . a r g s ! = None : def i n t e g r a n d ( t , y , tmp , myint= s e l f . integrand , myargs= s e l f . a r g s ) : tmp1 = apply ( myint , ( t , y ) + myargs ) r e t u r n tmp1 i f s e l f . b o r d e r a r g s ! = None : def border ( t , y , tmp , myb = s e l f . border , myargs= s e l f . b o r d e r a r g s ) : r e t u r n apply (myb , ( t , y ) + myargs ) # Handle a v e c t o r i n p u t o f y r e s u l t = None i f type ( t ) = = Numeric . a r r a y t y p e : r e s u l t = Numeric . z e r o s ( ( l e n ( s e l f . y ) , t . shape [ 0 ] ) , Numeric . F l o a t ) # C a l l t h e FORTRAN r o u t i n e s e l f . p r a e ( ) # W r i t e Common B l o c k s i f r e s u l t = = None : # Only o n e datum f o r t y = self .y t0 = s e l f . t tmp = l s o d a r . l s o d a r ( integrand , y , t0 , t , # Type o f t o l e r a n c e s and t o l e r a n c e s s e l f . itol , s e l f . rtol , s e l f . atol , # T a s k and s t a t u s o f c a l c u l a t i o n task , s e l f . s t a t e , # O p t i o n a l p a r a m t e r s and work a r r a y s i o p t , s e l f . rwork , s e l f . iwork , # J a c o b i matrix , e x t e r n a l not supported y e t jacobi , jt , # Border c o n d i t i o n s border , s e l f . borderdimension ) yout , t , s t a t e , j r o o t = tmp else : # handle a vector y = self .y

36

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

# Do v e c t o r o p e r a t i o n s . . . f o r i in Numeric . arange ( t . shape [ 0 ] ) : if i == 0: t0 = s e l f . t else : t 0 = t [ i 1] tmp= l s o d a r . l s o d a r ( integrand , y , t0 , t [ i ] , # Type o f t o l e r a n c e s and t o l e r a n c e s s e l f . itol , s e l f . rtol , s e l f . atol , # T a s k and s t a t u s o f c a l c u l a t i o n task , s e l f . s t a t e , # O p t i o n a l p a r a m t e r s and work a r r a y s i o p t , s e l f . rwork , s e l f . iwork , # J a c o b i matrix , e x t e r n a l not supported y e t jacobi , jt , # Border c o n d i t i o n s border , s e l f . borderdimension ) y , tend , s t a t e , j r o o t = tmp result [: , i ] = y if state < 1: # E r r o r > r a i s e E r r o r break if state == 3: break # I m p o r t a n t : Must b e i n s i d e f o r l o o p self . state = state # End Of f o r l o o p t o b i g l o o p . . i f s t a t e = = 2 or s t a t e = = 1 : yout = r e s u l t elif state == 3: # S a v e t h e t i m e when a b o r d e r was e n c o u n t e r e d t [ i ] = tend t = t [ : i +1] yout = r e s u l t [ : , : i +1] # End o f i f t o o o o b i g c o n d i t i o n a l

self .

p o s t ( ) # S a v e Common B l o c k S t a t u s

# Handle e r r o r s f r o m t h e r o u t i n e if state < 1: r a i s e ValueError , msgs [ s t a t e ] i f r e s u l t = = None : s e l f . y = yout self . t = t else : s e l f . y = yout [ : , 1 ] s e l f . t = t [ 1] if state == 3: # Border condition . . . self . state = 2 r e t u r n yout , j r o o t , t self . state = state r e t u r n yout , None , None

def D e r i v a t i v e ( s e l f , t , order ) : C a l c u l a t e s t h e order d e r i v a t i v e a t t h e value t . !!!!!!!!!!!!!!!!!!!! !!! Warning !!!! !!!!!!!!!!!!!!!!!!!!

37

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387

Can only be used a f t e r t h e c a l c u l a t i o n ! a s s e r t ( s e l f . s t a t e = = 2 or s e l f . s t a t e = = 3 ) s e l f . prae ( ) tmp = l s o d a r . i n t d y ( t , order , s e l f . rwork , s e l f . dimension ) self . post () r e t u r n tmp def SaveCommonBlockData ( s e l f ) : # Load common s i n f o r m a t i o n t o a r r a y s l s o d a r . s r c a r ( s e l f . rsav , s e l f . i s a v , 1 ) StoreCommonBlockData ( s e l f ) : # R e s t o r e common s f r o m a r r a y s l s o d a r . s r c a r ( s e l f . rsav , s e l f . i s a v , 2 ) prae ( s e l f ) : # print id of l a s t j o b : , id ( s e l f . l a s t j o b ) , # p r i n t P r i n t e d from p r a e : , i d ( s e l f ) tmp = s e l f . l a s t j o b . G e t L a s t J o b I d ( ) # J o b i d s l o w e r t h a n 0 mean n o t h i n g n e e d s t o b e d o n e i f tmp ! = s e l f . j o b i d and tmp > 0 : # Tell the other job to save i t s data s e l f . l a s t j o b . GetJob ( tmp ) . SaveCommonBlockData ( ) s e l f . StoreCommonBlockData ( ) s e l f . needpostcleanup = tmp else : s e l f . needpostcleanup = 0 # print s e l f . needpostcleanup = , s e l f . needpostcleanup s e l f . l a s t j o b . SetActiveJobId ( s e l f . jobid ) def post ( self ) : # print id of l a s t j o b : , id ( s e l f . l a s t j o b ) , # p r i n t P r i n t e d from p o s t : , i d ( s e l f ) i f s e l f . needpostcleanup > 0 : s e l f . SaveCommonBlockData ( ) tmp = s e l f . l a s t j o b . GetJob ( s e l f . needpostcleanup ) tmp . StoreCommonBlockData ( ) s e l f . l a s t j o b . S e t A c t i v e J o b I d ( s e l f . needpostcleanup ) s e l f . needpostcleanup = 1 e l i f s e l f . needpostcleanup = = 0 : pass else : r a i s e TypeError , s e l f . needpostcleanup i n i n s t a n c e + id ( s e l f ) + \ should be 0 or 1 \ n but was found t o : + \ s e l f . needpostcleanup

def

def

Listing 11: lsodar/ClassIntegrateExpert.py


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # ! / u s r / b i n / env p y t h o n # # Author : P i e r r e S c h n i z e r < P i e r r e . S c h n i z e r @ c e r n . ch > # Date : 05. 11. 2002 # P u r p o s e : To i l l u s t r a t e t h e u s a g e and c a p a b i l i t y o f f 2 p y # V e r s i o n : Not e v e n a l p h a . T h i s i s i l l u s t r a t i o n c o d e , s o do n o t u s e i t i n # production environement # L i c e n s e : LGPL # import Numeric import C l a s s I n t e g r a t e Integrate = ClassIntegrate . Integrate class IntegrateExpert ( Integrate ) : This c l a s s uses t h e same i n t e r n a l alghorithm as t h e c l a s s i n t e g r a t e ,

38

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

but i t o f f e r s many methods f o r f i n e tuning . def

Us eOptionalInput ( s e l f , p r e c i s i o n ) : I n t e r n a l Method . # l s o d a r uses a v a r i a b l e to f i n d out i f i t should i n v e s t i g a t e the # a d d i t i o n a l input paramters . These are honoured h er e . s e l f . iopt = 1 # Make l s o d a r n o t i c e t h e c h a n g e . . . s t a t e 2 means , t h a t i n i t was # a l l r e a d y done . if self . state == 2: self . state = 3

def S e t F i r s t S t e p S i z e ( s e l f , s i z e ) : The s t e p s i z e t o be attempted on t h e f i r s t s t e p . t h e d e f a u l t value i s determined by t h e s o l v e r . This method can only be c a l l e d b e f o r e t h e f i r s t calculation ! if self . state ! = 1 : r a i s e TypeError , This method can only be c a l l e d b e f o r e t h e problem i s e v a l u a t e d ! s e l f . rwork [ 5 1 ] = s i z e s e l f . Us eOptionalInput ( ) def SetMaximumStepSize ( s e l f , s i z e ) : t h e maximum a b s o l u t e s t e p s i z e allowed . t h e d e f a u l t value i s i n f i n i t e . s e l f . rwork [ 6 1 ] = s i z e s e l f . Us eOptionalInput ( ) def SetMinimumStepSize ( s e l f , s i z e ) : t h e minimum a b s o l u t e s t e p s i z e allowed . t h e d e f a u l t value i s 0 . ( t h i s lower bound i s not e n f o r c e d on t h e f i n a l s t e p b e f o r e r e a c h i n g t c r i t when t a s k = 4 or 5 . ) s e l f . rwork [ 7 1 ] = s i z e s e l f . Us eOptionalInput ( ) def PrintAtMethodSwitch ( s e l f , bool ) : f l a g t o g e n e r a t e e x t r a p r i n t i n g a t method s w i t c h e s . i x p r = 0 means no e x t r a p r i n t i n g ( t h e d e f a u l t ) . i x p r = 1 means p r i n t data on each sw itch . t , h , and n s t w i l l be p r i n t e d on t h e same l o g i c a l u n i t as used f o r e r r o r messages . Allowed Input 0 and 1 Warning : This output w i l l go t o t h e d e v i c e / f i l e with l o g i c a l number 6 and thus depends which FORTRAN compiler t r a n s l a t e d t h e l s o d a r extension . a s s e r t ( bool = = 0 or bool = = 1 ) s e l f . iwork [ 5 1 ] = bool s e l f . Us eOptionalInput ( ) def SetMaximumNumberOfSteps ( s e l f , s t e p ) : Maximum number o f ( i n t e r n a l l y d e f i n e d ) s t e p s

39

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

allowed during one c a l l t o t h e s o l v e r . t h e d e f a u l t value i s 5 0 0 . a s s e r t ( step > 0) s e l f . iwork [ 6 1 ] = s t e p s e l f . Us eOptionalInput ( ) def SetMaximumNumberOfMessages ( s e l f , s t e p ) : Maximum number o f messages p r i n t e d ( per problem ) warning t h a t t + h = t on a s t e p ( h = s t e p s i z e ) . t h i s must be p o s i t i v e t o r e s u l t i n a nond e f a u l t value . t h e d e f a u l t value i s 1 0 . # a s s e r t ( s t e p > 0) s e l f . iwork [ 7 1 ] = s t e p s e l f . UseOpt ionalInput ( ) def SetMaximumOrderForNonStiffSolver ( s e l f , order ) : The maximum order t o be allowed f o r t h e n o n s t i f f ( adams ) method . t h e d e f a u l t value i s 1 2 . Higher v a l u e s a r e not a c c e p t e d . This value i s held c o n s t a n t during t h e problem . a s s e r t ( order < = 1 2 or order > = 0 ) s e l f . iwork [ 8 1 ] = order s e l f . UseOpt ionalInput ( ) def SetMaximumOrderForStiffSolver ( s e l f , order ) : t h e maximum order t o be allowed f o r t h e s t i f f ( bdf ) method . t h e d e f a u l t value i s 5 . Higher v a l u e s a r e not a c c e p t e d . This value i s held c o n s t a n t during t h e problem . a s s e r t ( order < = 5 or order > = 0 ) s e l f . iwork [ 9 1 ] = order s e l f . UseOpt ionalInput ( ) def G e t L a s t S t e p S i z e ( s e l f ) : t h e s t e p s i z e i n t l a s t used ( s u c c e s s f u l l y ) r e t u r n s e l f . rwork [ 1 1 1 ] def GetNextStepSize ( s e l f ) : t h e s t e p s i z e t o be attempted on t h e n ext s t e p r e t u r n s e l f . rwork [ 1 2 1 ] def GetCurrentValueOfIndependentVariable ( s e l f ) : t h e c u r r e n t value o f t h e independent v a r i a b l e which t h e s o l v e r has a c t u a l l y reached , i . e . t h e c u r r e n t i n t e r n a l mesh p o i n t i n t . on output , t c u r w i l l always be a t l e a s t as f a r as t h e argument t , but may be f a r t h e r ( i f i n t e r p o l a t i o n was done ) . r e t u r n s e l f . rwork [ 1 3 1 ] def G e t T o l e r a n c e F a c t o r ( s e l f ) : a t o l e r a n c e s c a l e f a c t o r , g r e a t e r than 1 . 0 , computed when a r e q u e s t f o r too much a c c u r a c y was detected ( s t a t e = 3 i f detected at the s t a r t of t h e problem , s t a t e = 2 o t h e r w i s e ) . i f the r e l a t i v e

40

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221

and t h e a b s o l u t e a c c u r a c y a r e uniformly s c a l e d up by a f a c t o r o f t o l s f f o r t h e n ext c a l l , then t h e s o l v e r i s deemed l i k e l y t o succeed . ( t h e user may a l s o i g n o r e t o l s f and a l t e r t h e t o l e r a n c e parameters i n any o t h e r way a p p r o p r i a t e . ) r e t u r n s e l f . rwork [ 1 4 1 ] def GetValueAtLastMethodSwitch ( s e l f ) : t h e value o f t a t t h e time o f t h e l a s t method switch , i f any r e t u r n s e l f . rwork [ 1 5 1 ] def GetNumberOfBorderEvaluations ( s e l f ) : r e t u r n s e l f . iwork [ 1 0 1 ] def GetNumberOfSteps ( s e l f ) : t h e number o f s t e p s taken f o r t h e problem so f a r . r e t u r n s e l f . iwork [ 1 1 1 ] def GetNumberOfIntegrandEvaluations ( s e l f ) : t h e number o f i n t e g r a n d e v a l u a t i o n s f o r t h e problem so f a r . r e t u r n s e l f . iwork [ 1 2 1 ] def GetNumberOfJacobiEvaluations ( s e l f ) : t h e number o f j a c o b i a n e v a l u a t i o n s ( and o f matrix l u decompositions ) f o r t h e problem so f a r . r e t u r n s e l f . iwork [ 1 3 1 ] def GetMethodOrder ( s e l f ) : t h e method order l a s t used ( s u c c e s s f u l l y ) r e t u r n s e l f . iwork [ 1 4 1 ] def GetNextMethodOrder ( s e l f ) : t h e method order attempted on t h e next s t e p r e t u r n s e l f . iwork [ 1 5 1 ] def GetIndexOfLargestErrorMagnitude ( s e l f ) : t h e index o f t h e component o f l a r g e s t magnitude i n t h e weighted l o c a l e r r o r v e c t o r ( e ( i ) /ewt ( i ) ) , on an e r r o r r e t u r n with s t a t e = 4 or 5. r e t u r n s e l f . iwork [ 1 6 1 ] def GetMethodOfLastStep ( s e l f ) : t h e method i n d i c a t o r f o r t h e l a s t s u c c e s s f u l s t e p . . 1 means adams ( n o n s t i f f ) , 2 means bdf ( s t i f f ) . tmp = s e l f . iwork [ 1 9 1 ] i f tmp = = 1 : s t r i n g = non s t i f f else : string = s t i f f r e t u r n tmp , s t r i n g

41

222 223 224 225 226 227 228 229 230 231 232 233 234

def GetMethodOfNextStep ( s e l f ) : t h e method i n d i c a t o r f o r t h e ne xt s t e p . . 1 means adams ( n o n s t i f f ) , 2 means bdf ( s t i f f ) . tmp = s e l f . iwork [ 1 9 1 ] i f tmp = = 1 : s t r i n g = non s t i f f else : string = s t i f f r e t u r n tmp , s t r i n g # End C l a s s I n t e g r a t e E x p e r t

Listing 12: An example using the objects


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 # ! / u s r / b i n / env p y t h o n # # Author : P i e r r e S c h n i z e r < P i e r r e . S c h n i z e r @ c e r n . ch > # Date : 05. 11. 2002 # P u r p o s e : To i l l u s t r a t e t h e u s a g e and c a p a b i l i t y o f f 2 p y # V e r s i o n : Not e v e n a l p h a . T h i s i s i l l u s t r a t i o n c o d e , s o do n o t u s e i t i n # production environement # import time import copy import sys import Numeric import Gnuplot import l s o d a r Integrate = lsodar . Integrate IntegrateExpert = lsodar . IntegrateExpert gravity = 9 . 8 1 def BulletMovement ( t , vec , weight , r e s i s t a n c e ) : Use t h e e q u a t i o n d e s c i b i n g a b u l l e t x vx y vy ... ... ... ... vec [ 0 ] vec [ 1 ] vec [ 2 ] vec [ 3 ]

# s y s . s t d e r r . w r i t e ( BM1 + s t r ( t ) + \ n ) vec = copy . copy ( vec ) x vx y vy = = = = vec [ 0 ] vec [ 1 ] vec [ 2 ] vec [ 3 ]

tmp = Numeric . ones ( 4 , Numeric . F l o a t ) tmp [ 0 ] = vx tmp [ 1 ] = r e s i s t a n c e vx tmp [ 2 ] = vy tmp [ 3 ] = weight g r a v i t y + r e s i s t a n c e vy r e t u r n tmp

def Border ( t , vec , b u l l e t , t2 , r e s u l t , index = [ 1 , ] , l a s t =Numeric . z e r o s ( ( 5 , 5 ) , Numeric . F l o a t ) , l a s t i n d e x = [ 0 , ] ) : b u l l e t d e s c r i b e s t h e movement o f t h e second b u l l e t

42

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

t = copy . copy ( t ) vec = copy . copy ( vec )

# C a l c u l a t e t h e p o s t i o n f o r a l l t 2 s which a r e n o t r e a c h e d y e t t i n d e x = index [ 0 ] i f t > t 2 [ t i n d e x ] and t < = t 2 [ 1 ] : i n d i c e s = Numeric . l e s s e q u a l ( t 2 [ t i n d e x : ] , t ) ttmp = Numeric . compress ( i n d i c e s , t 2 [ t i n d e x : ] ) index [ 0 ] = i n t ( index [ 0 ] ) + l e n ( ttmp ) r t = l i s t ( b u l l e t ( ttmp ) [ 0 ] ) r t = Numeric . t r a n s p o s e ( Numeric . a r r a y ( r t ) ) map( r e s u l t . append , r t ) # Get t h e p o s i t i o n o f t h e s e c o n d b u l l e t r 2 = b u l l e t . GetCurrentValueOfIndependentVariable ( ) r1 = r2 b u l l e t . GetLastStepSize ( ) i f t <r 1 : # T o u t o f s c o p e we h a v e t o r e c a l c u l a t e test = t last [0 , :] i = Numeric . argmin ( Numeric . c l i p ( t e s t , 0 . 0 , f l o a t ( I n f ) ) ) ttmp = l a s t [ 0 , i ] tvec = l a s t [ 1 : , i ] b u l l e t . R e i n i t ( tvec , ttmp ) tmp = b u l l e t ( t ) else : tmp = b u l l e t ( t ) last [0 , lastindex [ 0 ] ] = t l a s t [ 1 : , l a s t i n d e x [ 0 ] ] = tmp [ 0 ] lastindex [ 0 ] = ( lastindex [ 0 ] + 1 ) % 5

a s s e r t ( tmp [ 1 ] = = None ) a s s e r t ( tmp [ 2 ] = = None ) pos = copy . copy ( tmp [ 0 ] ) tmp = pos [ : : 2 ] vec [ : : 2 ] tmp1 = Numeric . sum ( tmp 2 ) + tmp [ 0 ] r e t u r n tmp1 def run ( ) : t = Numeric . arange ( 1 0 0 0 ) / 1 0 0 0 . 0 2 result2 = [ ] b u l l e t 2 = I n t e g r a t e E x p e r t ( BulletMovement , Numeric . a r r a y ( ( 2 5 . , 1 0 , 0 , 1 0 ) ) , 0 , args = ( 1 . 0 , . 1 ) ) b u l l e t 1 = I n t e g r a t e E x p e r t ( BulletMovement , Numeric . a r r a y ( ( 0 . , 1 0 , 0 . 0 , 1 0 ) ) , 0 , a r g s = ( 1 . 0 , 0 . 1 ) , border=Border , b o r d e r a r g s =( b u l l e t 2 , t , r e s u l t 2 ) )

tmp , j r o o t , t 1 = b u l l e t 1 ( t ) i f t 1 ! = None : t = t1 r e s u l t = Numeric . t r a n s p o s e ( tmp ) r e s u l t 2 = Numeric . a r r a y ( r e s u l t 2 )

tmp = t [ : l e n ( r e s u l t 2 [ : , 0 ] ) ] x = result2 [: ,0]

g = Gnuplot . Gnuplot ( ) command = s e t grid

43

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

s e t x l a b e l x [m] s e t y l a b e l y [m] g ( command )

g . p l o t ( Gnuplot . Data ( r e s u l t [ : , 0 ] , r e s u l t [ : , 2 ] , t i t l e = b1 , with= l i n e ) , Gnuplot . Data ( r e s u l t 2 [ : , 0 ] , r e s u l t 2 [ : , 2 ] , t i t l e = b2 , with= l i n e ) , ) p r i n t r e s u l t . shape p r i n t r e s u l t 2 . shape raw input ( )

if

name run ( )

==

main

44

Vous aimerez peut-être aussi