改变fortran中的数组维度

发布于 2024-10-25 21:45:05 字数 737 浏览 1 评论 0原文

在 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

我想知道是否有一种好的方法可以使用第二个假定的形状接口来完成此类操作,而无需使用复制它。

There are basically two ways to pass arrays to a subroutine in 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

where you need an explicit interface for the second, usually through the use of a module.

From FORTRAN77, I'm used to the first alternative, and I read this is also the most efficient if you pass the whole array.

The nice thing with the explicit shape is that I can also call a subroutine and treat the array as a vector instead of a matrix:

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

I wondered if there is a nice way to do that kind of thing using the second, assumed shape interface, without copying it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

甜宝宝 2024-11-01 21:45:05

请参阅 RESHAPE 内在函数,例如

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

或者,如果您想避免复制(在某些情况下,优化编译器可能能够在不复制的情况下进行重塑,例如,如果之后不使用 RHS 数组,但我不会指望它),从 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

See the RESHAPE intrinsic, e.g.

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

Alternatively, if you want to avoid the copy (in some cases an optimizing compiler might be able to do a reshape without copying, e.g. if the RHS array is not used afterwards, but I wouldn't count on it), as of Fortran 2003 you can assign pointers to targets of different rank, using bounds remapping. E.g. something like

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
¢蛋碎的人ぎ生 2024-11-01 21:45:05

我想做同样的事情并遇到了这个讨论。这些解决方案都不适合我的目的,但我发现,如果您使用当前 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

I was looking to do the same thing and came across this discussion. None of the solutions suited my purposes, but I found that there is a way to reshape an array without copying the data using iso_c_binding if you are using the fortran 2003 standard which current fortran 90/95 compilers tend to support. I know the discussion is old, but I figured I would add what I came up with for the benefit of others with this question.

The key is to use the function C_LOC to convert an array to an array pointer, and then use C_F_POINTER to convert this back into a fortran array pointer with the desired shape. One challenge with using C_LOC is that C_LOC only works for array that have a directly specified shape. This is because arrays in fortran with an incomplete size specification (i.e., that use a : for some dimension) include an array descriptor along with the array data. C_LOC does not give you the memory location of the array data, but the location of the descriptor. So an allocatable array or a pointer array don't work with C_LOC (unless you want the location of the compiler specific array descriptor data structure). The solution is to create a subroutine or function that receives the array as an array of fixed size (the size really doesn't matter). This causes the array variable in the function (or subroutine) to point to the location of the array data rather than the location of the array descriptor. You then use C_LOC to get a pointer to the array data location and C_F_POINTER to convert this pointer back into an array with the desired shape. The desired shape must be passed into this function to be used with C_F_POINTER. Below is an example:

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
潇烟暮雨 2024-11-01 21:45:05

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

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

编辑:
我使用 ifort 11、gfortran 4.5 和 gfortran 4.6 测试了@janneb 的示例程序。在这三个中,它仅适用于 gfortran 4.6。有趣的是,要走向另一个方向并将一维数组连接到现有的二维数组,需要 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

@janneb has already answered re RESHAPE. RESHAPE is a function -- usually used in an assignment statement so there will be a copy operation. Perhaps it can be done without copying using pointers. Unless the array is huge, it is probably better to use RESHAPE.

I'm skeptical that the explicit shape array is more efficient than the assumed shape, in terms of runtime. My inclination is to use the features of the Fortran >=90 language and use assumed shape declarations ... that way you don't have to bother passing the dimensions.

EDIT:
I tested the sample program of @janneb with ifort 11, gfortran 4.5 and gfortran 4.6. Of these three, it only works in gfortran 4.6. Interestingly, to go the other direction and connect a 1-D array to an existing 2-D array requires another new feature of Fortran 2008, the "contiguous" attribute -- at least according to gfortran 4.6.0 20110318. Without this attribute in the declaration, there is a compile time error.

    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
梦忆晨望 2024-11-01 21:45:05

您可以使用假定大小的数组,但这可能意味着多层包装
例程:

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

You can use assumed-size arrays, but it can mean multiple layers of wrapper
routines:

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
云柯 2024-11-01 21:45:05

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

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

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

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

I am using ifort 14.0.3 and 2D to 1D conversion, I could use an allocatable array for 2D array and a pointer array for 1D:

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

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

As @M.S.B mentioned, in case both A and AP have the pointer attribute, I had to use contiguous attribute for A to guarantee the consistency of the conversion.

饭团 2024-11-01 21:45:05

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' 可以解决 'rout1' 的问题 4,但不能解决 '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 的是“我还不知道尺寸大小,但我们会修复它。”。但这需要一个备用的整数argumentmnt(或者我们可以转换为整数的其他东西)来愚弄gfortran。

Gfortran is a bit paranoid with interfaces. It not only wants to know the type, kind, rank and number of arguments, but also the shape, the target attribute and the intent (although I agree with the intent part). I encountered a similar problem.

With gfortran, there are three different dimension definition:
1. Fixed
2. Variable
3. Assumed-size

With ifort, categories 1 and 2 are considered the same, so you can do just define any dimension size as 0 in the interface and it works.

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 complains about the interface. Changing the 0 into 'sz' solves the problem four 'rout1', but not for 'rout2'.

However, you can fool gfortran around and say dimension(0:10+0*sz) instead of dimension(0:10) and gfortran compiles and gives the same
result as ifort.

This is a stupid trick and it relies on the existence of the integer 'sz' that may not be there. Another program:

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

This works under ifort for the same reasons as the previous example, but gfortran complains about the interface. I do not know how I can fix it.

The only thing I want to tell gfortran is 'I do not know the dimension size yet, but we will fix it.'. But this needs a spare integer arguemnt (or something else that we can turn into an integer) to fool gfortran around.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文