изменение размеров массива в фортране - PullRequest
14 голосов
/ 23 марта 2011

Существует два основных способа передачи массивов в подпрограмму в 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 ]

8 голосов
/ 11 ноября 2013

Я хотел сделать то же самое и наткнулся на это обсуждение. Ни одно из решений не соответствовало моим целям, но я обнаружил, что есть способ изменить форму массива без копирования данных, используя iso_c_binding, если вы используете стандарт fortran 2003, который обычно поддерживают современные компиляторы fortran 90/95. Я знаю, что дискуссия старая, но я решил добавить то, что придумал, для блага других с этим вопросом.

Ключ заключается в том, чтобы использовать функцию C_LOC для преобразования массива в указатель массива, а затем использовать C_F_POINTER для преобразования этого обратно в указатель массива fortran с желаемой формой. Одна из проблем, связанных с использованием C_LOC, заключается в том, что C_LOC работает только для массива, который имеет непосредственно указанную форму. Это связано с тем, что массивы в фортране с неполной спецификацией размера (то есть использующие: для некоторого измерения) включают дескриптор массива вместе с данными массива. 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
8 голосов
/ 23 марта 2011

См. Встроенную переменную RESHAPE, например,

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

В качестве альтернативы, если вы хотите избежать копирования (в некоторых случаях оптимизирующий компилятор может изменить форму без копирования,например, если массив RHS впоследствии не использовался, но я бы на это не рассчитывал), начиная с Fortran 2003, вы можете назначать указатели для целей различного ранга, используя baps remapping .Например, что-то вроде

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
6 голосов
/ 23 марта 2011

@ janneb уже ответил re RESHAPE. RESHAPE - это функция, обычно используемая в операторе присваивания, поэтому будет операция копирования. Возможно, это можно сделать без копирования с помощью указателей. Если массив не огромен, возможно, лучше использовать RESHAPE.

Я скептически отношусь к тому, что массив явных фигур более эффективен, чем предполагаемая форма, с точки зрения времени выполнения. Я склонен использовать возможности языка Fortran> = 90 и использовать декларации предполагаемой формы ... таким образом, вам не нужно беспокоиться о передаче измерений.

EDIT: Я протестировал пример программы @janneb с ifort 11, gfortran 4.5 и gfortran 4.6. Из этих трех он работает только в 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
1 голос
/ 07 июля 2011

Вы можете использовать массивы предполагаемого размера, но это может означать несколько слоев процедур-оболочек:

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
0 голосов
/ 11 ноября 2016

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', но не для '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.

0 голосов
/ 27 января 2016

Я использую 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, чтобы гарантировать согласованность преобразования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...