更改fortran中的数组维度

时间:2011-03-23 13:33:53

标签: fortran

基本上有两种方法可以将数组传递给Fortran 90/95中的子程序:

PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY

SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT

SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED

你需要第二个显式接口,通常是通过使用模块。

从FORTRAN77开始,我习惯了第一种选择,如果你传递整个数组,我认为这也是最有效的。

显式形状的好处是我也可以调用子程序并将数组视为向量而不是矩阵:

SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT

我想知道是否有一种很好的方法可以使用第二个假定的形状界面来做这种事情而不复制它。

6 个答案:

答案 0 :(得分:7)

参见RESHAPE内在,例如

http://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html

或者,如果你想避免复制(在某些情况下,优化编译器可能能够在不复制的情况下进行重构,例如,如果之后不使用RHS数组,但我不会指望它),您可以使用 bounds remapping 为Fortran 2003分配指向不同等级目标的指针。例如。

之类的东西
program ptrtest
  real, pointer :: a(:)
  real, pointer :: b(:,:)
  integer :: n = 10
  allocate(a(n**2))
  a = 42
  b (1:n, 1:n) => a
end program ptrtest

答案 1 :(得分:7)

我当时也想做同样的事情并且遇到了这个讨论。没有一个解决方案适合我的目的,但我发现如果你使用当前fortran 90/95编译器倾向于支持的fortran 2003标准,有一种方法可以重塑数组而不使用iso_c_binding复制数据。我知道讨论已经过时了,但我想我会在这个问题上为其他人的利益添加我想出的内容。

关键是使用函数C_LOC将数组转换为数组指针,然后使用C_F_POINTER将其转换回具有所需形状的fortran数组指针。使用C_LOC的一个挑战是C_LOC仅适用于具有直接指定形状的数组。这是因为fortran中具有不完整大小规范的数组(即,使用:对于某些维度)包括数组描述符以及数组数据。 C_LOC不会为您提供数组数据的内存位置,而是提供描述符的位置。因此,可分配数组或指针数组不能与C_LOC一起使用(除非您需要编译器特定数组描述符数据结构的位置)。解决方案是创建一个子例程或函数,将数组作为固定大小的数组接收(大小确实无关紧要)。这会导致函数(或子例程)中的数组变量指向数组数据的位置,而不是数组描述符的位置。然后使用C_LOC获取指向数组数据位置的指针,使用C_F_POINTER将此指针转换回具有所需形状的数组。必须将所需的形状传递给此函数以与C_F_POINTER一起使用。以下是一个例子:

program arrayresize
  implicit none
  integer, allocatable :: array1(:)
  integer, pointer :: array2(:,:)

  ! allocate and initialize array1
  allocate(array1(6))
  array1 = (/1,2,3,4,5,6/)

  ! This starts out initialized to 2
  print *, 'array1(2) = ', array1(2)

  ! Point array2 to same data as array1. The shape of array2
  ! is passed in as an array of intergers because C_F_POINTER
  ! uses and array of intergers as a SIZE parameter.
  array2 => getArray(array1, (/2,3/))

  ! Change the value at array2(2,1) (same as array1(2))
  array2(2,1) = 5

  ! Show that data in array1(2) was modified by changing
  ! array2(2,1)
  print *, 'array(2,1) = array1(2) = ', array1(2)

contains

  function getArray(array, shape_) result(aptr)
    use iso_c_binding, only: C_LOC, C_F_POINTER
    ! Pass in the array as an array of fixed size so that there
    ! is no array descriptor associated with it. This means we
    ! can get a pointer to the location of the data using C_LOC
    integer, target :: array(1)
    integer :: shape_(:)
    integer, pointer :: aptr(:,:)

    ! Use C_LOC to get the start location of the array data, and
    ! use C_F_POINTER to turn this into a fortran pointer (aptr).
    ! Note that we need to specify the shape of the pointer using an
    ! integer array.
    call C_F_POINTER(C_LOC(array), aptr, shape_)
  end function
end program

答案 2 :(得分:6)

@janneb已经回答了重新回复。 RESHAPE是一个函数 - 通常在赋值语句中使用,因此将有一个复制操作。也许它可以在不使用指针进行复制的情况下完成。除非数组很大,否则最好使用RESHAPE。

我怀疑显式形状数组在运行时方面比假设形状更有效。我倾向于使用Fortran> = 90语言的功能并使用假定的形状声明......这样您就不必费心去传递尺寸了。

编辑: 我用ifort 11,gfortran 4.5和gfortran 4.6测试了@janneb的示例程序。在这三个中,它仅适用于gfortran 4.6。有趣的是,要转向另一个方向并将1-D阵列连接到现有的2-D阵列,需要Fortran 2008的另一个新功能,即“连续”属性 - 至少根据gfortran 4.6.0 20110318.在没有此属性的情况下声明,有一个编译时错误。

    program test_ptrs

   implicit none

   integer :: i, j

   real, dimension (:,:), pointer, contiguous :: array_twod
   real, dimension (:), pointer :: array_oned

   allocate ( array_twod (2,2) )

   do i=1,2
      do j=1,2
         array_twod (i,j) = i*j
      end do
   end do

   array_oned (1:4) => array_twod

   write (*, *) array_oned

   stop

end program test_ptrs

答案 3 :(得分:1)

您可以使用假定大小的数组,但它可以表示多层包装器 例程:

program test

  implicit none

  integer :: test_array(10,2)

  test_array(:,1) = (/1,   2,  3,  4,  5,  6,  7,  8,  9, 10/)
  test_array(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/)

  write(*,*) "Original array:"
  call print_a(test_array)

  write(*,*) "Reshaped array:"
  call print_reshaped(test_array, size(test_array))

contains

  subroutine print_reshaped(a, n)
  integer, intent(in) :: a(*)
  integer, intent(in) :: n
  call print_two_dim(a, 2, n/2)
  end subroutine

  subroutine print_two_dim(a, n1, n2)
  integer, intent(in) :: a(1:n1,1:*)
  integer, intent(in) :: n1, n2
  call print_a(a(1:n1,1:n2))
  end subroutine

  subroutine print_a(a)
  integer, intent(in) :: a(:,:)
  integer :: i
  write(*,*) "shape:", shape(a)
  do i = 1, size(a(1,:))
      write(*,*) a(:,i)
  end do
  end subroutine

end program test

答案 4 :(得分:0)

我正在使用ifort 14.0.3和2D到1D的转换,我可以为2D数组使用可分配数组,为1D使用指针数组:

integer,allocatable,target :: A(:,:)
integer,pointer :: AP(:)

allocate(A(3,N))
AP(1:3*N) => A

正如@ M.S.B所提到的,如果A和AP都有指针属性,我必须使用A的连续属性来保证转换的一致性。

答案 5 :(得分:0)

Gfortran对界面有点偏执。它不仅想知道参数的类型,种类,等级和数量,还要知道形状,目标属性和意图(尽管我同意意图部分)。我遇到了类似的问题。

使用gfortran,有三种不同的维度定义:
1.固定
2.变量
3.假设大小

使用ifort,类别1和2被认为是相同的,因此您可以在界面中将任何尺寸大小定义为0并且它可以正常工作。

program test

  implicit none

  integer, dimension(:), allocatable :: ownlist

  interface
    subroutine blueprint(sz,arr)
      integer, intent(in) :: sz
      integer, dimension(0), intent(in) :: arr
      ! This zero means that the size does not matter,
      ! as long as it is a one-dimensional integer array.
    end subroutine blueprint
  end interface

  procedure(blueprint), pointer :: ptr

  allocate(ownlist(3))
  ownlist = (/3,4,5/)
  ptr => rout1
  call ptr(3,ownlist)
  deallocate(ownlist)

  allocate(ownlist(0:10))
  ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
  ptr => rout2
  call ptr(3,ownlist)
  deallocate(ownlist)

contains

  ! This one has a dimension size as input.
  subroutine rout1(sz,arr)
    implicit none
    integer, intent(in) :: sz
    integer, dimension(sz), intent(in) :: arr
    write(*,*) arr
    write(*,*) arr(1)
  end subroutine rout1

  ! This one has a fixed dimension size.
  subroutine rout2(sz,arr)
    implicit none
    integer, intent(in) :: sz
    integer, dimension(0:10), intent(in) :: arr
    write(*,*) "Ignored integer: ",sz
    write(*,*) arr
    write(*,*) arr(1)
  end subroutine rout2

end program test

Gfortran抱怨界面。将0改为' sz'解决问题4' rout1',但不适用于' rout2'。

然而,你可以欺骗gfortran并说尺寸(0:10 + 0 * sz)而不是尺寸(0:10)和gfortran编译并给出相同的 结果为ifort。

这是一个愚蠢的伎俩,它依赖于整数' sz'那可能不存在。另一个计划:

program difficult_test

  implicit none

  integer, dimension(:), allocatable :: ownlist

  interface
    subroutine blueprint(arr)
      integer, dimension(0), intent(in) :: arr
    end subroutine blueprint
  end interface

  procedure(blueprint), pointer :: ptr

  allocate(ownlist(3))
  ownlist = (/3,4,5/)
  ptr => rout1
  call ptr(ownlist)
  deallocate(ownlist)

  allocate(ownlist(0:10))
  ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
  ptr => rout2
  call ptr(ownlist)
  deallocate(ownlist)

contains

  subroutine rout1(arr)
    implicit none
    integer, dimension(3), intent(in) :: arr
    write(*,*) arr
    write(*,*) arr(1)
  end subroutine rout1

  subroutine rout2(arr)
    implicit none
    integer, dimension(0:10), intent(in) :: arr
    write(*,*) arr
    write(*,*) arr(1)
  end subroutine rout2

end program difficult_test

这在ifort下工作的原因与上一个例子相同,但gfortran抱怨接口。我不知道如何解决它。

我唯一要告诉gfortran的是'我还不知道尺寸大小,但我们会修复它。'。但是这需要一个备用的整数arguemnt(或其他我们可以变成整数的东西)来欺骗gfortran。