Passing numpy string-format arrays to fortran using f2py

旧巷老猫 提交于 2019-12-19 03:15:13

问题


My aim is to print the 2nd string from a python numpy array in fortran, but I only ever get the first character printed, and it's not necessarily the right string either.

Can anyone tell me what the correct way to pass full string arrays to fortran?

The code is as follows:

testpy.py

import numpy as np
import testa4

strvar = np.asarray(['aa','bb','cc'], dtype = np.dtype('a2'))
testa4.testa4(strvar)

testa4.f90

subroutine testa4(strvar)
implicit none

character(len=2), intent(in) :: strvar(3)
!character*2 does not work here - why?

print *, strvar(2)

end subroutine testa4

Compiled with

f2py -c -m testa4 testa4.f90

Output of above code

c

Desired output

bb

回答1:


I don't know how to do that using f2py. But it can be done with ctypes. You get an array of characters, but you can convert it to a string very easily.

subroutine testa4(strvar) bind(C, name='testa4')
  use iso_c_binding
  implicit none

  character(len=1,kind=c_char), intent(in) :: strvar(2,3)

  print *, strvar(:,2)

end subroutine testa4

compile: gfortran -shared -fPIC testa4.f90 -o testa4.so

import numpy as np
import ctypes

testa4 = ctypes.CDLL("./testa4.so")

strvar = np.asarray(['aa','bb','cc'], dtype = np.dtype('a2'))
strvar_p = ctypes.c_void_p(strvar.ctypes.data)

testa4.testa4(strvar_p)

run:

> python testpy.f90 
 bb



回答2:


Per the documentation, f2py likes string arrays to be passed with dtype='c' (i.e., '|S1'). This gets you part of the way there, although there are some oddities with array shape going on behind the scenes (e.g., in a lot of my tests I found that fortran would keep the 2 character length, but interpret the 6 characters as being indicative of a 2x6 array, so I'd get random memory back in the output). This (as far as I could tell), requires that you treat the Fortran array as a 2D character array (as opposed to a 1D "string" array). Unfortunately, I couldn't get it to take assumed shape and ended up passing the number of strings in as an argument.

I'm pretty sure I'm missing something fairly obvious, but this should work for the time being. As to why CHARACTER*2 doesn't work ... I honestly have no idea.

MODULE char_test

CONTAINS

SUBROUTINE print_strings(strings, n_strs)
    IMPLICIT NONE

    ! Inputs
    INTEGER, INTENT(IN) :: n_strs
    CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings

!f2py INTEGER, INTENT(IN) :: n_strs
!f2py CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings

    ! Misc.
    INTEGER*4 :: j


    DO j=1, n_strs
        WRITE(*,*) strings(:,j)
    END DO

END SUBROUTINE print_strings

END MODULE char_test

----------------

import numpy as np
import char_test as ct

strings = np.array(['aa', 'bb', 'cc'], dtype='c').T
ct.char_test.print_strings(strings, strings.shape[1])

strings = np.array(['ab', 'cd', 'ef'], dtype='c').T
ct.char_test.print_strings(strings, strings.shape[1]) 

-->python run_char_test.py
 aa
 bb
 cc
 ab
 cd
 ef



回答3:


Not really an answer but too long to comment: maybe this helps..

in your first case what gets passed in to fortran is for some reason the first character of each string:

'abc'

which in fortran ends up in the length 2 arrays as 'ab' , 'c'. If you work strictly with length one string arrays all is well, I suppose. Unfortunately you cant fake the system and split into a single character array in python ['a','a','b','b'..- it throws an error if the array lengths don't match.

On your second question, if you declare with the

  character*2

notation, it actually works to simply pass a regular python string list:

  testa4.testa4(['aa','bb','cc'])

(and now throws an error if you try the numpy string array.). The strings must be the exact correct length or it throws an error here as well.



来源:https://stackoverflow.com/questions/22293180/passing-numpy-string-format-arrays-to-fortran-using-f2py

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!