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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With