Pass array of structs from Python to Fortran

Earlier today I posted about passing struct from Python to Fortran Pass struct from Python to Fortran that was kindly solved by @PierU

Now I am trying to pass an array of structs (kind of related topic here: Passing an array of structs from C to Fortran)

I think I am doing it correctly, and sometimes it works. Other times doesn’t.

Python side:

import ctypes as ct
import numpy as np

libtest = ct.CDLL('./pass_struct_array.so') 
pass_struct_array = libtest.pass_struct_array

class StructTest(ct.Structure):
    _fields_ = [
                ("nonzeros", ct.POINTER(ct.c_int)),
                ("values", ct.POINTER(ct.c_double)),
                ("n_nonzeros", ct.c_int),
               ]

nnz = 400

def set_struct_term():
    values = np.zeros((2,nnz), dtype=np.float_, order="F")
    nonzeros = np.zeros((2,nnz), dtype=np.intc, order="F")

    for i in range(nnz):
        values[0,i] = i
        values[1,i] = 2*i
        nonzeros[0,i] = i
        nonzeros[1,i] = 2*i

    v_ptr = values.ctypes.data_as(ct.POINTER(ct.c_double))
    nnz_ptr = nonzeros.ctypes.data_as(ct.POINTER(ct.c_int))

    return StructTest(nnz_ptr,
                           v_ptr,
                           ct.c_int(nnz),
                           )

if __name__ == "__main__":
    n_terms = 2

    not_working_terms = (StructTest * n_terms)()
    for i in range(2):
        not_working_terms[i] = set_struct_term()
     
# Create a double array, pass it to Fotran as a pointer
    values = 10*np.ones((2,nnz), dtype=np.float_, order="F")
    values[0,0] = 1.
    values[0,1] = 2.
    values[0,2] = 3.
    values[1,0] = 11.
    values[1,1] = 12.
    values[1,2] = 13.

    values[0,:] = 13.3
    values[1,:] = np.arange(1.,(nnz+10)/10.,0.1)
    values_ptr = values.ctypes.data_as(ct.POINTER(ct.c_double))

    nonzeros = np.ones((2,nnz), dtype=np.intc, order="F")
    nonzeros[0,0] = 1
    nonzeros[0,1] = 2
    nonzeros[0,2] = 3
    nonzeros_ptr = nonzeros.ctypes.data_as(ct.POINTER(ct.c_int))

    values2 = 20*np.ones((2,nnz), dtype=np.float_, order="F")
    values2[0,0] = 2.
    values2[0,1] = 3.
    values2[0,2] = 4.
    values2[1,0] = 21.
    values2[1,1] = 31.
    values2[1,2] = 41.
    values2_ptr = values2.ctypes.data_as(ct.POINTER(ct.c_double))

    nonzeros2 = np.ones((2,nnz), dtype=np.intc, order="F")
    nonzeros2[0,0] = 2
    nonzeros2[0,1] = 3
    nonzeros2[0,2] = 4
    nonzeros2_ptr = nonzeros2.ctypes.data_as(ct.POINTER(ct.c_int))

    working_terms = (StructTest * n_terms)()
    working_terms[0] = StructTest(nonzeros_ptr, values_ptr, ct.c_int(nnz))
    working_terms[1] = StructTest(nonzeros2_ptr, values2_ptr, ct.c_int(nnz))

    pass_struct_array.argtypes = [
                     ct.POINTER(StructTest * n_terms), ct.c_int,
            ]
    pass_struct_array(
                     ct.byref(not_working_terms), ct.c_int(n_terms),
                   )

Fortran:

subroutine pass_struct_array(struct_terms, n_terms) bind(C, name="pass_struct_array")
  use iso_c_binding
  implicit none

  type, bind(c) :: struct_element
    type(c_ptr) :: nonzeros
    type(c_ptr) :: values
    integer(c_int) :: n_nonzeros
  end type struct_element

  real(c_double), pointer :: struct_element_values_ptr(:, :)
  integer(c_int), pointer :: struct_element_nonzeros_ptr(:,:)

  type(struct_element), intent(in) :: struct_terms(n_terms)
  integer(c_int), intent(in), value :: n_terms

  integer :: i

  do i = 1, n_terms
    call c_f_pointer(struct_terms(i) % values,   struct_element_values_ptr,   [2, struct_terms(i) % n_nonzeros])
    call c_f_pointer(struct_terms(i) % nonzeros, struct_element_nonzeros_ptr, [2, struct_terms(i) % n_nonzeros])

    print "('values(1,:)',/,10(F5.2,x))", struct_element_values_ptr(1,:)
    print "('values(2,:)',/,10(F5.2,x))", struct_element_values_ptr(2,:)

    print "('nonzeros(1,:)',/,10(i0,x))", struct_element_nonzeros_ptr(1,:)
    print "('nonzeros(2,:)',/,10(i0,x))", struct_element_nonzeros_ptr(2,:)

  end do

end subroutine pass_struct_array

Compilation with gfortran -shared pass_struct_array.f90 -o pass_struct_array.so.

There are two ways of passing the array of structs in the Python code. In some cases the array pass works and in some other cases it does not:

  1. using not_working_terms works if nnz is small (for example 30)
  2. using not_working_terms does not work if nnz is large (for example 400)
  3. using working_terms works even with larger nnz (for example 400)

Could you help me to figure out why the other way does not always work?

Edit. By not working I mean, I get garbage like this:

nonzeros(2,:)
1076533657 1072693248 1076533657 1072798105 1076533657 1072902963 1076533657 1073007820 1076533657 1073112678
1076533657 1073217536 1076533657 1073322393 1076533657 1073427251 1076533657 1073532108 1076533657 1073636966
1076533657 1073741824 1076533657 1073794252 1076533657 1073846681 1076533657 1073899110 1076533657 1073951539
1076533657 1074003968 1076533657 1074056396 1076533657 1074108825 1076533657 1074161254 1076533657 1074213683
1076533657 1074266112 1076533657 1074318540 1076533657 1074370969 1076533657 1074423398 1076533657 1074475827
1076533657 1074528256 1076533657 1074580684 1076533657 1074633113 1076533657 1074685542 1076533657 1074737971
1076533657 1074790400 1076533657 1074816614 1076533657 1074842828 1076533657 1074869043 1076533657 1074895257
1076533657 1074921472 1076533657 1074947686 1076533657 1074973900 1076533657 1075000115 1076533657 1075026329
1076533657 1075052544 1076533657 1075078758 1076533657 1075104972 1076533657 1075131187 1076533657 1075157401
1076533657 1075183616 1076533657 1075209830 1076533657 1075236044 1076533657 1075262259 1076533657 1075288473
1076533657 1075314688 1076533657 1075340902 1076533657 1075367116 1076533657 1075393331 1076533657 1075419545
1076533657 1075445760 1076533657 1075471974 1076533657 1075498188 1076533657 1075524403 1076533657 1075550617
1076533657 1075576832 1076533657 1075603046 1076533657 1075629260 1076533657 1075655475 1076533657 1075681689
1076533657 1075707904 1076533657 1075734118 1076533657 1075760332 1076533657 1075786547 1076533657 1075812761
1076533657 1075838976 1076533657 1075852083 1076533657 1075865190 1076533657 1075878297 1076533657 1075891404
1076533657 1075904512 1076533657 1075917619 1076533657 1075930726 1076533657 1075943833 1076533657 1075956940
1076533657 1075970048 1076533657 1075983155 1076533657 1075996262 1076533657 1076009369 1076533657 1076022476
1076533657 1076035584 1076533657 1076048691 1076533657 1076061798 1076533657 1076074905 1076533657 1076088012
1076533657 1076101120 1076533657 1076114227 1076533657 1076127334 1076533657 1076140441 1076533657 1076153548
1076533657 1076166656 1076533657 1076179763 1076533657 1076192870 1076533657 1076205977 1076533657 1076219084
1076533657 1076232192 1076533657 1076245299 1076533657 1076258406 1076533657 1076271513 1076533657 1076284620
1076533657 1076297728 1076533657 1076310835 1076533657 1076323942 1076533657 1076337049 1076533657 1076350156
1076533657 1076363264 1076533657 1076376371 1076533657 1076389478 1076533657 1076402585 1076533657 1076415692
1076533657 1076428800 1076533657 1076441907 1076533657 1076455014 1076533657 1076468121 1076533657 1076481228
1076533657 1076494336 1076533657 1076507443 1076533657 1076520550 1076533657 1076533657 1076533657 1076546764
1076533657 1076559872 1076533657 1076572979 1076533657 1076586086 1076533657 1076599193 1076533657 1076612300
1076533657 1076625408 1076533657 1076638515 1076533657 1076651622 1076533657 1076664729 1076533657 1076677836
1076533657 1076690944 1076533657 1076704051 1076533657 1076717158 1076533657 1076730265 1076533657 1076743372
1076533657 1076756480 1076533657 1076769587 1076533657 1076782694 1076533657 1076795801 1076533657 1076808908
1076533657 1076822016 1076533657 1076835123 1076533657 1076848230 1076533657 1076861337 1076533657 1076874444
1076533657 1076887552 1076533657 1076894105 1076533657 1076900659 1076533657 1076907212 1076533657 1076913766
1076533657 1076920320 1076533657 1076926873 1076533657 1076933427 1076533657 1076939980 1076533657 1076946534
1076533657 1076953088 1076533657 1076959641 1076533657 1076966195 1076533657 1076972748 1076533657 1076979302
1076533657 1076985856 1076533657 1076992409 1076533657 1076998963 1076533657 1077005516 1076533657 1077012070
1076533657 1077018624 1076533657 1077025177 1076533657 1077031731 1076533657 1077038284 1076533657 1077044838
1076533657 1077051392 1076533657 1077057945 1076533657 1077064499 1076533657 1077071052 1076533657 1077077606
1076533657 1077084160 1076533657 1077090713 1076533657 1077097267 1076533657 1077103820 1076533657 1077110374
1076533657 1077116928 1076533657 1077123481 1076533657 1077130035 1076533657 1077136588 1076533657 1077143142
1076533657 1077149696 1076533657 1077156249 1076533657 1077162803 1076533657 1077169356 1076533657 1077175910
1076533657 1077182464 1076533657 1077189017 1076533657 1077195571 1076533657 1077202124 1076533657 1077208678

I’m no Python expert, but what are not_working_terms and working_terms in terms of Python objects? Are they lists or np arrays?

That is a good question. I am no expert myself, but I understood that they are somehow representing an array of ctype structs:

not_working_terms = (StructTest * n_terms)()

Where

class StructTest(ct.Structure): 
    _fields_ = [ ("nonzeros", ct.POINTER(ct.c_int)), 
    ("values", ct.POINTER(ct.c_double)),
    ("n_nonzeros", ct.c_int), ]

So I guess it means that in the array, there are n_terms elements of StructTest structs allocated.

The struct above would be something like this on c side:

struct StructTest {
    int *nonzeros;
    double *values;
    int n_nonzeros;
};

And then the not_working_terms and working_terms would be an array of size 2 of those.

I tried ifx and the result is the same.

Edit. and I tried flang too. Same result.

Hmm… If I do this instead it seems to work:

    not_working_terms = (StructTest * n_terms)()
    for j in range(2):
        values = np.zeros((2,nnz), dtype=np.float_, order="F")
        nonzeros = np.zeros((2,nnz), dtype=np.intc, order="F")

        for i in range(nnz):
            values[0,i] = i
            values[1,i] = 2*i
            nonzeros[0,i] = i
            nonzeros[1,i] = 2*i

        v_ptr = values.ctypes.data_as(ct.POINTER(ct.c_double))
        nnz_ptr = nonzeros.ctypes.data_as(ct.POINTER(ct.c_int))

        not_working_terms[j]= StructTest(nnz_ptr,
                               v_ptr,
                               ct.c_int(nnz),
                               )

So calling a function to return the StructTest reference fails it.
I wonder if this is a some sort of bug in Python…

Yes, definitely it is the return of StructTest reference from a function that screws it up:
This works too:

def set_struct_term():
    values = np.zeros((2,nnz), dtype=np.float_, order="F")
    nonzeros = np.zeros((2,nnz), dtype=np.intc, order="F")

    for i in range(nnz):
        values[0,i] = i
        values[1,i] = 2*i
        nonzeros[0,i] = i
        nonzeros[1,i] = 2*i

    v_ptr = values.ctypes.data_as(ct.POINTER(ct.c_double))
    nnz_ptr = nonzeros.ctypes.data_as(ct.POINTER(ct.c_int))

    return nnz_ptr, v_ptr, ct.c_int(nnz)

and

    not_working_terms = (StructTest * n_terms)()
    for i in range(2):
        nnz_ptr, v_ptr, nnz_c_int = set_struct_term()
        not_working_terms[i] = StructTest(nnz_ptr, v_ptr, nnz_c_int)

Actually not… I didn’t look carefully enough. So the first set of values are still garbage with both of the above:

values(1,:)
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    1.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
     NaN     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     1.00     0.00     0.00      NaN      NaN      NaN      NaN     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    1.00 ********     0.00     1.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
values(2,:)
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00      NaN      NaN      NaN      NaN     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     1.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00
nonzeros(1,:)
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 348 417
41520816 1900125808 352 353 354 355 356 357 358 359
360 361 362 363 364 365 366 367 368 369
370 371 372 373 374 375 376 377 378 379
380 381 382 383 384 385 386 387 388 389
390 391 392 393 394 395 396 397 398 399
nonzeros(2,:)
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 696 0
0 32676 704 706 708 710 712 714 716 718
720 722 724 726 728 730 732 734 736 738
740 742 744 746 748 750 752 754 756 758
760 762 764 766 768 770 772 774 776 778
780 782 784 786 788 790 792 794 796 798
values(1,:)
    0.00     1.00     2.00     3.00     4.00     5.00     6.00     7.00     8.00     9.00
   10.00    11.00    12.00    13.00    14.00    15.00    16.00    17.00    18.00    19.00
   20.00    21.00    22.00    23.00    24.00    25.00    26.00    27.00    28.00    29.00
   30.00    31.00    32.00    33.00    34.00    35.00    36.00    37.00    38.00    39.00
   40.00    41.00    42.00    43.00    44.00    45.00    46.00    47.00    48.00    49.00
   50.00    51.00    52.00    53.00    54.00    55.00    56.00    57.00    58.00    59.00
   60.00    61.00    62.00    63.00    64.00    65.00    66.00    67.00    68.00    69.00
   70.00    71.00    72.00    73.00    74.00    75.00    76.00    77.00    78.00    79.00
   80.00    81.00    82.00    83.00    84.00    85.00    86.00    87.00    88.00    89.00
   90.00    91.00    92.00    93.00    94.00    95.00    96.00    97.00    98.00    99.00
  100.00   101.00   102.00   103.00   104.00   105.00   106.00   107.00   108.00   109.00
  110.00   111.00   112.00   113.00   114.00   115.00   116.00   117.00   118.00   119.00
  120.00   121.00   122.00   123.00   124.00   125.00   126.00   127.00   128.00   129.00
  130.00   131.00   132.00   133.00   134.00   135.00   136.00   137.00   138.00   139.00
  140.00   141.00   142.00   143.00   144.00   145.00   146.00   147.00   148.00   149.00
  150.00   151.00   152.00   153.00   154.00   155.00   156.00   157.00   158.00   159.00
  160.00   161.00   162.00   163.00   164.00   165.00   166.00   167.00   168.00   169.00
  170.00   171.00   172.00   173.00   174.00   175.00   176.00   177.00   178.00   179.00
  180.00   181.00   182.00   183.00   184.00   185.00   186.00   187.00   188.00   189.00
  190.00   191.00   192.00   193.00   194.00   195.00   196.00   197.00   198.00   199.00
  200.00   201.00   202.00   203.00   204.00   205.00   206.00   207.00   208.00   209.00
  210.00   211.00   212.00   213.00   214.00   215.00   216.00   217.00   218.00   219.00
  220.00   221.00   222.00   223.00   224.00   225.00   226.00   227.00   228.00   229.00
  230.00   231.00   232.00   233.00   234.00   235.00   236.00   237.00   238.00   239.00
  240.00   241.00   242.00   243.00   244.00   245.00   246.00   247.00   248.00   249.00
  250.00   251.00   252.00   253.00   254.00   255.00   256.00   257.00   258.00   259.00
  260.00   261.00   262.00   263.00   264.00   265.00   266.00   267.00   268.00   269.00
  270.00   271.00   272.00   273.00   274.00   275.00   276.00   277.00   278.00   279.00
  280.00   281.00   282.00   283.00   284.00   285.00   286.00   287.00   288.00   289.00
  290.00   291.00   292.00   293.00   294.00   295.00   296.00   297.00   298.00   299.00
  300.00   301.00   302.00   303.00   304.00   305.00   306.00   307.00   308.00   309.00
  310.00   311.00   312.00   313.00   314.00   315.00   316.00   317.00   318.00   319.00
  320.00   321.00   322.00   323.00   324.00   325.00   326.00   327.00   328.00   329.00
  330.00   331.00   332.00   333.00   334.00   335.00   336.00   337.00   338.00   339.00
  340.00   341.00   342.00   343.00   344.00   345.00   346.00   347.00   348.00   349.00
  350.00   351.00   352.00   353.00   354.00   355.00   356.00   357.00   358.00   359.00
  360.00   361.00   362.00   363.00   364.00   365.00   366.00   367.00   368.00   369.00
  370.00   371.00   372.00   373.00   374.00   375.00   376.00   377.00   378.00   379.00
  380.00   381.00   382.00   383.00   384.00   385.00   386.00   387.00   388.00   389.00
  390.00   391.00   392.00   393.00   394.00   395.00   396.00   397.00   398.00   399.00
values(2,:)
    0.00     2.00     4.00     6.00     8.00    10.00    12.00    14.00    16.00    18.00
   20.00    22.00    24.00    26.00    28.00    30.00    32.00    34.00    36.00    38.00
   40.00    42.00    44.00    46.00    48.00    50.00    52.00    54.00    56.00    58.00
   60.00    62.00    64.00    66.00    68.00    70.00    72.00    74.00    76.00    78.00
   80.00    82.00    84.00    86.00    88.00    90.00    92.00    94.00    96.00    98.00
  100.00   102.00   104.00   106.00   108.00   110.00   112.00   114.00   116.00   118.00
  120.00   122.00   124.00   126.00   128.00   130.00   132.00   134.00   136.00   138.00
  140.00   142.00   144.00   146.00   148.00   150.00   152.00   154.00   156.00   158.00
  160.00   162.00   164.00   166.00   168.00   170.00   172.00   174.00   176.00   178.00
  180.00   182.00   184.00   186.00   188.00   190.00   192.00   194.00   196.00   198.00
  200.00   202.00   204.00   206.00   208.00   210.00   212.00   214.00   216.00   218.00
  220.00   222.00   224.00   226.00   228.00   230.00   232.00   234.00   236.00   238.00
  240.00   242.00   244.00   246.00   248.00   250.00   252.00   254.00   256.00   258.00
  260.00   262.00   264.00   266.00   268.00   270.00   272.00   274.00   276.00   278.00
  280.00   282.00   284.00   286.00   288.00   290.00   292.00   294.00   296.00   298.00
  300.00   302.00   304.00   306.00   308.00   310.00   312.00   314.00   316.00   318.00
  320.00   322.00   324.00   326.00   328.00   330.00   332.00   334.00   336.00   338.00
  340.00   342.00   344.00   346.00   348.00   350.00   352.00   354.00   356.00   358.00
  360.00   362.00   364.00   366.00   368.00   370.00   372.00   374.00   376.00   378.00
  380.00   382.00   384.00   386.00   388.00   390.00   392.00   394.00   396.00   398.00
  400.00   402.00   404.00   406.00   408.00   410.00   412.00   414.00   416.00   418.00
  420.00   422.00   424.00   426.00   428.00   430.00   432.00   434.00   436.00   438.00
  440.00   442.00   444.00   446.00   448.00   450.00   452.00   454.00   456.00   458.00
  460.00   462.00   464.00   466.00   468.00   470.00   472.00   474.00   476.00   478.00
  480.00   482.00   484.00   486.00   488.00   490.00   492.00   494.00   496.00   498.00
  500.00   502.00   504.00   506.00   508.00   510.00   512.00   514.00   516.00   518.00
  520.00   522.00   524.00   526.00   528.00   530.00   532.00   534.00   536.00   538.00
  540.00   542.00   544.00   546.00   548.00   550.00   552.00   554.00   556.00   558.00
  560.00   562.00   564.00   566.00   568.00   570.00   572.00   574.00   576.00   578.00
  580.00   582.00   584.00   586.00   588.00   590.00   592.00   594.00   596.00   598.00
  600.00   602.00   604.00   606.00   608.00   610.00   612.00   614.00   616.00   618.00
  620.00   622.00   624.00   626.00   628.00   630.00   632.00   634.00   636.00   638.00
  640.00   642.00   644.00   646.00   648.00   650.00   652.00   654.00   656.00   658.00
  660.00   662.00   664.00   666.00   668.00   670.00   672.00   674.00   676.00   678.00
  680.00   682.00   684.00   686.00   688.00   690.00   692.00   694.00   696.00   698.00
  700.00   702.00   704.00   706.00   708.00   710.00   712.00   714.00   716.00   718.00
  720.00   722.00   724.00   726.00   728.00   730.00   732.00   734.00   736.00   738.00
  740.00   742.00   744.00   746.00   748.00   750.00   752.00   754.00   756.00   758.00
  760.00   762.00   764.00   766.00   768.00   770.00   772.00   774.00   776.00   778.00
  780.00   782.00   784.00   786.00   788.00   790.00   792.00   794.00   796.00   798.00
nonzeros(1,:)
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
100 101 102 103 104 105 106 107 108 109
110 111 112 113 114 115 116 117 118 119
120 121 122 123 124 125 126 127 128 129
130 131 132 133 134 135 136 137 138 139
140 141 142 143 144 145 146 147 148 149
150 151 152 153 154 155 156 157 158 159
160 161 162 163 164 165 166 167 168 169
170 171 172 173 174 175 176 177 178 179
180 181 182 183 184 185 186 187 188 189
190 191 192 193 194 195 196 197 198 199
200 201 202 203 204 205 206 207 208 209
210 211 212 213 214 215 216 217 218 219
220 221 222 223 224 225 226 227 228 229
230 231 232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247 248 249
250 251 252 253 254 255 256 257 258 259
260 261 262 263 264 265 266 267 268 269
270 271 272 273 274 275 276 277 278 279
280 281 282 283 284 285 286 287 288 289
290 291 292 293 294 295 296 297 298 299
300 301 302 303 304 305 306 307 308 309
310 311 312 313 314 315 316 317 318 319
320 321 322 323 324 325 326 327 328 329
330 331 332 333 334 335 336 337 338 339
340 341 342 343 344 345 346 347 348 349
350 351 352 353 354 355 356 357 358 359
360 361 362 363 364 365 366 367 368 369
370 371 372 373 374 375 376 377 378 379
380 381 382 383 384 385 386 387 388 389
390 391 392 393 394 395 396 397 398 399
nonzeros(2,:)
0 2 4 6 8 10 12 14 16 18
20 22 24 26 28 30 32 34 36 38
40 42 44 46 48 50 52 54 56 58
60 62 64 66 68 70 72 74 76 78
80 82 84 86 88 90 92 94 96 98
100 102 104 106 108 110 112 114 116 118
120 122 124 126 128 130 132 134 136 138
140 142 144 146 148 150 152 154 156 158
160 162 164 166 168 170 172 174 176 178
180 182 184 186 188 190 192 194 196 198
200 202 204 206 208 210 212 214 216 218
220 222 224 226 228 230 232 234 236 238
240 242 244 246 248 250 252 254 256 258
260 262 264 266 268 270 272 274 276 278
280 282 284 286 288 290 292 294 296 298
300 302 304 306 308 310 312 314 316 318
320 322 324 326 328 330 332 334 336 338
340 342 344 346 348 350 352 354 356 358
360 362 364 366 368 370 372 374 376 378
380 382 384 386 388 390 392 394 396 398
400 402 404 406 408 410 412 414 416 418
420 422 424 426 428 430 432 434 436 438
440 442 444 446 448 450 452 454 456 458
460 462 464 466 468 470 472 474 476 478
480 482 484 486 488 490 492 494 496 498
500 502 504 506 508 510 512 514 516 518
520 522 524 526 528 530 532 534 536 538
540 542 544 546 548 550 552 554 556 558
560 562 564 566 568 570 572 574 576 578
580 582 584 586 588 590 592 594 596 598
600 602 604 606 608 610 612 614 616 618
620 622 624 626 628 630 632 634 636 638
640 642 644 646 648 650 652 654 656 658
660 662 664 666 668 670 672 674 676 678
680 682 684 686 688 690 692 694 696 698
700 702 704 706 708 710 712 714 716 718
720 722 724 726 728 730 732 734 736 738
740 742 744 746 748 750 752 754 756 758
760 762 764 766 768 770 772 774 776 778
780 782 784 786 788 790 792 794 796 798

So maybe I am doing something wrong?

I’m afraid I’m not good enough in Python (just a newbie) to really help here…

Just one thing: AFAIK numpy uses a row-major storage for the arrays, while Fortran uses column-major storage. In other words, when in Python you define

values = 10*np.ones((2,nnz), dtype=np.float_, order="F")

then in Fortran you should swap the dimensions:

    call c_f_pointer( struct_terms(i) % values,         &
                      struct_element_values_ptr,        &
                      [struct_terms(i) % n_nonzeros, 2] )

Thanks @PierU! I appreciate your help.

I have studied a bit this array storage thing in numpy. It seems that if one initializes like mentioned (with order="F") then Fortran array convention (with contiguous memory) is used: numpy.ascontiguousarray — NumPy v1.26 Manual

1 Like

I am not familiar with ctypes either (so just playing with the code to get experience), and this modified version seems to work with Ubuntu22 + gfortran-10:

def set_struct_term():

    values = np.zeros((2,nnz), dtype=np.float_, order="F")
    nonzeros = np.zeros((2,nnz), dtype=np.intc, order="F")

    for i in range(nnz):
        values[0,i] = i
        values[1,i] = 2*i
        nonzeros[0,i] = i
        nonzeros[1,i] = 2*i

    return nonzeros, values

if __name__ == "__main__":

    n_terms = 2

    not_working_terms = (StructTest * n_terms)()

    data_list = []
    for i in range(n_terms):
        data_list.append( set_struct_term() )

    for i in range(n_terms):
        nnz_ptr = data_list[i][0].ctypes.data_as(ct.POINTER(ct.c_int))
        v_ptr   = data_list[i][1].ctypes.data_as(ct.POINTER(ct.c_double))

        not_working_terms[i] = StructTest(nnz_ptr,
                                          v_ptr,
                                          ct.c_int(nnz),
                                          )

I am wondering how methods like .ctypes.data_as() affect the lifetime of Numpy arrays. In the original code, are the work arrays like nonzeros and values kept alive after the call of set_struct_term() (with no garbage collection)…? In the modified code, references to the work arrays are returned, so I guess they are kept alive. (But I’m not sure this is related to the unexpected behavior in the original code.)

1 Like

Nice work, sir! Thank you!
Seems to work perfectly now.

Looking at the codes, I don’t understand why the explicit referencing need to be done. But good thing to know in the Python/Fortran community. Thanks a lot!

Another pattern that seems to work is to save explicit python variables with a global dictionary and then return StructTest as in the original code:

# (header part omitted)
nnz = 10

mydat = {'values': [], 'nonzeros': []}  # <-- global dict

#------------------------------
def set_struct_term(iterm):

    values = np.zeros((2,nnz), dtype=np.float_, order="F")
    nonzeros = np.zeros((2,nnz), dtype=np.intc, order="F")

    mydat['values'].append(values)   # <-- save ref on python side
    mydat['nonzeros'].append(nonzeros)

    for i in range(nnz):
        values[0,i]   = i * 1 + 0.01 * iterm
        values[1,i]   = i * 2 + 0.01 * iterm
        nonzeros[0,i] = i * 10
        nonzeros[1,i] = i * 20

    nnz_ptr = nonzeros.ctypes.data_as(ct.POINTER(ct.c_int))
    v_ptr = values.ctypes.data_as(ct.POINTER(ct.c_double))

    return StructTest(nnz_ptr, v_ptr, ct.c_int(nnz) )

#------------------------------
if __name__ == "__main__":

    n_terms = 2
    terms = (StructTest * n_terms)()

    for it in range(n_terms):
        terms[it] = set_struct_term(iterm=it)
     
    pass_struct_array.argtypes = [
        ct.POINTER(StructTest * n_terms), ct.c_int,
    ]
    pass_struct_array( ct.byref(terms), ct.c_int(n_terms) )

I am not sure either, but my guess is that the memory created by np.zeros() may be automatically deallocated (or invalidated) by garbage collector etc when the local python variables / references go out of the scope (like automatic deallocation of local allocatable arrays in Fortran). If so, it might be necessary to keep their references somehow for the duration of the calculation. But the above guess may also be wrong… (the reason may be related to the usage of ctypes, for example).

(Also, I am assuming that .ctypes.data_as() just creates a pointer-like thing, without affecting the lifetime of python variables. The situation might be similar to c_loc() applied to local allocatable arrays in Fortran. But this assumption may be wrong either…)

1 Like