Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop python code in (Fortran) module error using f2py?

I am creating a Python module in Fortran using f2py. I would like produce an error (including error message) in the Python program if an error is encountered in the Fortran module. Consider the following example:

Fortran code (test.f):

subroutine foo(a,m)

  integer :: m,i
  integer, dimension(m) :: a
  !f2py intent(in) :: m
  !f2py intent(in,out) :: a
  !f2py intent(hide), depend(a) :: m=shape(a)

  do i = 1,m
    if ( a(i) .eq. 0 ) then
      print*, 'ERROR HERE..?'
    end if 
    a(i) = a(i)+1
  end do

end subroutine

This very simple program adds 1 to each element of a. But should produce an error if a(i) equal to zero. The accompanying Python code:

import test

print test.foo(np.array([1,2],dtype='uint32'))
print test.foo(np.array([0,2],dtype='uint32'))

The output is now:

[2 3]
ERROR HERE..?
[1 3]

But I want the Python program to hold on the error. Please help.

Answer

The stop command in Fortran does exactly this. Consider the updated Fortran code:

subroutine foo(a,m)

  integer :: m,i
  integer, dimension(m) :: a
  !f2py intent(in) :: m
  !f2py intent(in,out) :: a
  !f2py intent(hide), depend(a) :: m=shape(a)

  do i = 1,m
    if ( a(i) .eq. 0 ) then
      print*, 'Error from Fortran'
      stop
    end if 
    a(i) = a(i)+1
  end do

end subroutine

The output is now:

[2 3]
Error from Fortran

I.e. the Python code does not continue after the error.

like image 768
Tom de Geus Avatar asked Aug 02 '13 15:08

Tom de Geus


2 Answers

I've suggested to the numpy community to add add an extra f2py "enhancement" (raise_python_exception) which makes it possible to define a string variable in Fortran that if non-empty will cause Python to raise an exception once the function returns.

So in Fortran you would write something like:

subroutine calc_dq(q, temp, dq, error_mesg)
  !f2py raise_python_exception error_mesg

  real, intent(in) :: q, temp
  real, intent(out) :: dq

  character(len=100), intent(out) :: error_mesg

  if (.not. init_called()) then
     error_mesg = "`init` hasn't been called."
  else
     call q_flux_function(q, temp, dq)
  endif
end subroutine calc_dq

And called from Python the content of the error_mesg variable is used as the content of the exception:

In [2]: calc_dq(1.0, 300.)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-8-c0ce0cb9cda1> in <module>()
----> 1 calc_dq(1.0, 300.)

Exception: `init` hasn't been called.

I think this is quite a convenient way of raising exceptions from Fortran as it allows the exception message to be easily defined. I have put my suggestion on github.

like image 199
leifdenby Avatar answered Sep 19 '22 13:09

leifdenby


f2py does provide some statements that can be used to raise exceptions. See details here:

http://cens.ioc.ee/projects/f2py2e/usersguide/#statements

In particular, look at callstatement which describes how to add f2py_success = 0 which will trigger an exception.

I'm not sure this will help you with debugging the internals of the fortran library, but at least it is a start.

like image 44
IanSR Avatar answered Sep 16 '22 13:09

IanSR