Python和Fortran与ctypes:包含对象的列表

huangapple go评论85阅读模式
英文:

Python and fortran with ctypes: lists with objects

问题

当我调用lists_change函数时,出现了错误“OSError: exception: access violation reading 0x0000000000000005”。请告诉我如何修复它以及代码是否总体上编写正确?

当你调用lists_change函数时出现这个错误,通常表示在Fortran代码中存在内存访问问题。错误消息 "access violation reading 0x0000000000000005" 指示尝试读取无效内存地址。

这个问题可能与你在Fortran代码中使用指针时出现的问题有关。在你的Fortran代码中,你使用了C指针传递Python中的数组,但出现了内存访问错误。为了修复这个问题,你可以尝试以下几种方法:

  1. 确保Python和Fortran中的数据结构匹配:确保Python中的StateVector和Fortran中的state_vector数据结构匹配,包括大小和成员的顺序。

  2. 检查内存分配和释放:确保在Fortran代码中正确分配和释放内存。你已经使用了C_F_POINTER来分配内存,但你需要确保它与Python中的数组一致,并在使用完后释放内存。

  3. 检查函数调用参数:确保你正确传递了参数给lists_change函数。特别注意检查传递给length参数的值是否正确,它应该等于数组的大小。

  4. 调试Fortran代码:使用调试工具来检查Fortran代码中的问题。你可以使用调试器来跟踪代码执行并查找错误。

总之,这个问题通常是由内存访问问题引起的,因此你需要仔细检查代码中的指针操作和内存分配/释放,以确保数据在Python和Fortran之间正确传递。如果问题仍然存在,你可能需要进一步调试代码以找出问题的根本原因。

英文:

I need to write a library in fortran that will take an array of objects from Python, process it and return it back.

I've tried a lot of options, the last one looks like this:

Code Python:

import ctypes as ct
import os
import numpy as np

here = os.path.dirname(__file__)
lib_file = os.path.abspath(os.path.join(here, '..', 'fortran', 'svector_test_mod.dll'))
fortlib = ct.CDLL(lib_file, winmode=0)


class StateVector(ct.Structure):
    _fields_ = [
        ('x', ct.c_longdouble),
        ('y', ct.c_longdouble),
        ('z', ct.c_longdouble),
        ('vx', ct.c_longdouble),
        ('vy', ct.c_longdouble),
        ('vz', ct.c_longdouble),
        ('t', ct.c_longdouble),
    ]


size = 5
array = (StateVector * size)()

for i in range(size):
    array[i].x = ct.c_longdouble(2.5)
    array[i].y = ct.c_longdouble(2.5)
    array[i].z = ct.c_longdouble(2.5)
    array[i].vx = ct.c_longdouble(2.5)
    array[i].vy = ct.c_longdouble(2.5)
    array[i].vz = ct.c_longdouble(2.5)
    array[i].t = ct.c_longdouble(2.5)


np_objects = np.ctypeslib.as_array(array)

arr_ptr = np_objects.ctypes.data_as(ct.POINTER(ct.c_void_p))

fortlib.lists_change(ct.byref(arr_ptr), size)

Code Fortran:

module state_vector_mod
    use, intrinsic :: iso_c_binding, only: C_LONG_DOUBLE, c_int, C_PTR, C_F_POINTER
    implicit none

    type, bind(C) :: state_vector
        real(C_LONG_DOUBLE) :: x
        real(C_LONG_DOUBLE) :: y
        real(C_LONG_DOUBLE) :: z
        real(C_LONG_DOUBLE) :: vx
        real(C_LONG_DOUBLE) :: vy
        real(C_LONG_DOUBLE) :: vz
        real(C_LONG_DOUBLE) :: t
    end type state_vector

    contains

    subroutine make_sv(x, y, z, vx, vy, vz, t, sv) bind(c, name='make_sv')
        real(C_LONG_DOUBLE), intent(in) :: x
        real(C_LONG_DOUBLE), intent(in) :: y
        real(C_LONG_DOUBLE), intent(in) :: z
        real(C_LONG_DOUBLE), intent(in) :: vx
        real(C_LONG_DOUBLE), intent(in) :: vy
        real(C_LONG_DOUBLE), intent(in) :: vz
        real(C_LONG_DOUBLE), intent(in) :: t
        type(state_vector), intent(out), target :: sv

        sv = state_vector(x, y, z, vx, vy, vz, t)
    end subroutine make_sv

    subroutine lists_change(in_list, length) bind(C, name='lists_change')
        type(C_PTR), intent(inout) :: in_list
        !type(state_vector), dimension(length), intent(out) :: out_list
        type(state_vector), dimension(:), pointer :: my_array
        integer(c_int), intent(in) :: length
        integer(c_int) :: i

        call C_F_POINTER(in_list, my_array, [length])

        DO i = 1, length
            my_array(i)%x = my_array(i)%x * 2
            my_array(i)%y = my_array(i)%y * 2
            my_array(i)%z = my_array(i)%z * 2
            my_array(i)%vx = my_array(i)%vx * 2
            my_array(i)%vy = my_array(i)%vy * 2
            my_array(i)%vz = my_array(i)%vz * 2
            my_array(i)%t = my_array(i)%t * 2
        END DO

        !out_list = my_array
        deallocate(my_array)
    end subroutine lists_change
end module state_vector_mod

But when I call the lists_change function, an error appears "OSError: exception: access violation reading 0x0000000000000005"

Please tell me how you can fix it and whether the code is written correctly in general?

答案1

得分: 1

错误消息中的地址很奇怪。它指向一个整数值为5的地址值。变量size具有此值。我们可以得出结论,参数length

integer(c_int), intent(in) :: length

实际上应该具有value属性

integer(c_int), value :: length

或者它可以通过Python的引用方式(ct.byref)来调用。

关于一般性问题,francescalus是对的,in_list数组指针被修改,因此可能应该以值的方式传递,这样你就可以摆脱ct.byref或者使用intent(in)。或者你也可以只是通过引用传递数组本身,避免进行强制转换和解引用指针。

type(state_vector), intent(inout) :: my_array(length)

或者如果你引入一个单独的out_list参数并将其注释掉,那么需要进行一些明显的修改。

英文:

The address in the error message is peculiar. It points to an integer value of 5 being used as an address value. The variable size has this value. We can conclude that the argument length

    integer(c_int), intent(in) :: length

should actually have the value attribute

    integer(c_int), value :: length

or that it should be called from Python by reference (ct.byref).

Regarding general points, francescalus is right that the in_list array pointer is modified and should likely be passed as value so you can get rid of the ct.byref or with intent(in). Or you can also just pass the array itself by reference and avoid casting de-referencing the pointer.

    type(state_vector), intent(inout) :: my_array(length)

or with some obvious modifications if you introduce a separate out_list argument that is now commented out.

huangapple
  • 本文由 发表于 2023年7月20日 15:03:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76727408.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定