1
votes

I'm trying to use a Fortran90 numerical model with Python 3.5 using f2py.

f2py -c module.f90 -m mod --fcompiler=gfortran

The model is a Fortran module that contains variables, functions and subroutines. I'm posting here a code example that has all the features of the numerical model

module modul

implicit none
integer :: inte
real :: re
integer, dimension(3) :: array
CHARACTER(*), PARAMETER :: chara = "helloWorld"

contains
    integer function fun()
        fun = array(1) + array(2) + array(3)
    end function

    subroutine subrout(a,b)
        real, intent(out) :: b
        integer, intent(out) :: a
        a = inte + fun()
        b = re
        write(*,*) chara
    end subroutine subrout

end module modul

The conversion of the code with f2py works properly but, when I import the module in Python, I receive a segmentation fault error

>>> import mod
Segmentation fault (core dumped)

I realized that the problem depends on the unspecified dimension of the character array

CHARACTER(*), PARAMETER :: chara = "helloWorld"

as, if I remove that line of code or I assign a fixed dimension to the array (like CHARACTER(20) ), the module works properly in Python.

1. Is there a way to make it works without modifying the Fortran code (the model is long and complex and, if possible, I don't want to work on it)?

The character arrays are used to define strings in the code (like error messages) and to handle the input/output from files. One way to solve the problem (if there is no answer to question 1) could be defining a fixed maximum dimension for all the character strings (i.e. INTEGER, PARAMETER :: lenChar = 100) and then use

CHARACTER(lenChar), PARAMETER :: chara

instead of the previous declaration. In this case the module is successfully imported in Python but when I try to modify the array content it requires an input lenChar long (the same problem appears with the other types of array like INTEGER or REAL and doesn't depend by the PARAMETER attribute)

mod.modul.chara = "hello"
0-th dimension must be fixed to 100 but got 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: error return without exception set

This is an operation that the model needs to handle as the "chara" could be the path for the data and it needs to be initialized at run-time.

2. If there no answer to question (1) and I need to proceed in this way, how can I assign to the array an input shorter than the length of the array?

System information:
OS: Ubuntu 16.04
Python 3.5.2
f2py Version:     2
numpy Version: 1.12.1
GNU Fortran (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
2
Use tag fortran for all Fortran questions. You also use python and not python-3.5. - Vladimir F
Isn't Python 3 called using f2py3? Maybe only on my Linux distro... - Vladimir F
Assumed length character constants were legal in Fortran 90. F2py should understand them. - Vladimir F
Actually, the MCVE can be much shorter than your example. Three lines are enough. module modul; CHARACTER(*), PARAMETER :: chara = "helloWorld"; end module modul No array in sight. I don't get your point number (2). - Vladimir F
@VladimirF: (1) thanks for the correction (2) I've tried with f2py3 and f2py3.5 and it doesn't solve the problem (3) I don't get this comment (4) if I can't assign a variable length to the character than I have to decide at compile time fixed length long enough to contain any reasonable string. In this way if the string is shorter than the length then the code works, if the string is longer it doesn't work as there's no memory left. As you can see in the 6th block of code, Python requires the string to be as long as the length of the character. Is there a way to insert a short string? - Dalmo1991

2 Answers

1
votes

Answering question 2:

Strings in fortran are treated as array of characters in f2py. So in your case mod.modul.chara is an array of 100 characters (depending on the length specified in fortran). To feed something like 'hello' into the handle you could do the following:

for i,j in enumerate('hello'):
    mod.modul.chara[i]=j

This should let you pass the string.

1
votes

You seem to confuse variable length strings (whatever that may be, something like that is in Fortran 2003),string constants and dummy argument that take the length of what you pass. Probably you want:

subroutine sub(ch)
  character(*), intent(in) :: ch

  print *, ch
end

This is something completely different from your example. Here the argument ch will accept any length of string that you pass there.


The problem is in the assumed length character constant. This is enough to trigger the error

module m
  CHARACTER(*), PARAMETER :: chara = "helloWorld"
end module m

This is NOT a character array, it is a scalar character string of assumed length.

It is exactly equivalent to

module m
  CHARACTER(10), PARAMETER :: chara = "helloWorld"
end module m

The only difference is that the compiler gets the length automatically in the former case if you are lazy to count the length manually (or you are changing it from time to time).

The latter version works correctly in f2py:

f2py -c -m f2pychar f2pychar.f90 

ipython

In [1]: import f2pychar 

In [2]: print f2pychar.modul.chara
['h' 'e' 'l' 'l' 'o' 'W' 'o' 'r' 'l' 'd']

F2py should understand it, but it appears it does not. It is a serious bug in f2py. Just enter the length manually as in the latter example above. There is no difference, the code is euivalent.


To your number (2). It is NOT an array (in Fortran). The rules for arrays do not apply here. But more importantly, it is a constant, you cannot assign anything to it, so I don't really understand what you mean. If you have a character variable, you can assign a shorter string with no problem:

character(10) :: ch = "abcd"