Установить ранг массива во время выполнения - PullRequest
2 голосов
/ 21 сентября 2011

Я написал программу, которая читает файл, содержащий многомерные данные (чаще всего 3D, но также может иметь место и 2D).Чтобы повысить простоту, я хотел бы хранить данные в массиве того же ранга (или что-то, притворяющееся единым целым), т. Е. Используя трехмерный массив для трехмерных данных и т. Д .;проблема в том, что программа узнает о размерности только при чтении файла данных.

В настоящее время я храню все данные в массиве ранга один и вычисляю индекс каждого элемента в этом массиве по координатам элемента (это такжепредложил здесь ).Тем не менее, я также читал о переназначении ранга указателя, которое кажется очень элегантным и именно тем, что я искал, так как это позволило бы мне отказаться от моих процедур определения индекса массива (которые, вероятно, гораздо менее эффективны, чем то, что происходит засцены).Теперь, однако, похоже, что я сталкиваюсь с той же проблемой, что и при непосредственном объявлении многомерного массива - как сделать объявление?Опять же, для этого требуется информация о ранге.

Как я могу использовать переназначение ранга указателя или какой-то другой, более подходящий метод для установки ранга массива во время выполнения - в случае, если это вообще можно сделать.Или мне лучше придерживаться одного ранга, который я сейчас использую?

Ответы [ 4 ]

4 голосов
/ 21 сентября 2011

Однажды я спросил что-то похожее, то есть, как рассматривать двумерный массив как одно измерение, см. Здесь: изменение размеров массива в фортране .

Ответы были о присущем RESHAPE указателях, однако, похоже, нет никакого способа использовать тот же массив name , если вы не используете обертки подпрограмм, но тогда вам нужны обратные вызовы, чтобы конечная подпрограмма имела только одно имя, поэтому проблемы становятся больше.

program test
    real, allocatable :: data(:)
    allocate(data(n_data))
    ! read stuff, set is_2d and sizes
    if (is_2d) then
        call my_sub2(data, nX, nY)
    else
        call my_sub3(data, nX, nY, nZ)
    end if
end program test

subroutine my_sub2(data, nX, nY)
    real :: data(nx,nY)
    ! ...
end subroutine my_sub2

subroutine my_sub3(data, nX, nY, nZ)
    real :: data(nx,nY,nZ)
    ! ...
end subroutine my_sub3

РЕДАКТИРОВАТЬ: в качестве альтернативы установите третий ранг 1:

program test
    real, allocatable, target:: data(:)
    real, pointer:: my_array(:,:,:)
    logical is_2d
    n_data = 100
    allocate(data(n_data))
    ! read stuff, determine is_2d and n
    if (is_2d) then
        i=n
        j=n
        k=1
    else
        i=n
        j=n
        k=n
    end if
    my_array(1:i,1:j,1:k) => data
    write(*,*) my_array
end program test

Затем вы обрабатываете 2D-кейс как специальный 3D-кейс с третьим измерением 1.

EDIT2: также будьте осторожны при передаче несмежных массивов в подпрограммы с массивами явной формы: http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/lin/compiler_f/optaps/fortran/optaps_prg_arrs_f.htm

4 голосов
/ 22 сентября 2011

Вы можете использовать выражение EQUIVALENCE так:

Program ranks
    integer a_1d(12)
    integer a_2d(2, 6)
    integer a_3d(2, 2, 3)

    equivalence (a_1d, a_2d, a_3d)

    ! fill array 1d
    a_1d = (/1,2,3,4,5,6,7,8,9,10,11,12/)

    print *, a_1d

    print *, a_2d(1,1:6)
    print *, a_2d(2,1:6)

    print *, a_3d(1,1,1:3)
    print *, a_3d(2,1,1:3)
    print *, a_3d(1,2,1:3)
    print *, a_3d(2,2,1:3)

end program ranks
4 голосов
/ 21 сентября 2011

Если я правильно понимаю, вы считываете данные в массиве 1-D и хотите назначить его для 2D или 3D-массивов, которые вы знаете только после чтения файла.Почему бы не объявить как двумерные, так и трехмерные массивы как распределяемые массивы, а выделить только один из них на основе формы данных?Вы можете использовать встроенную функцию RESHAPE, чтобы сделать это удобно.

REAL,DIMENSION(:,:),  ALLOCATABLE :: arr2d
REAL,DIMENSION(:,:,:),ALLOCATABLE :: arr3d
...
! Read data into 1-D array, arr1d;
...
IF(L2d)THEN
  ALLOCATE(arr2d(im,jm))
  arr2d=RESHAPE(arr1d,(/im,jm/))
ELSEIF(L3d)THEN
  ALLOCATE(arr3d(im,jm,km))
  arr3d=RESHAPE(arr1d,(/im,jm,km/))
ENDIF
0 голосов
/ 08 апреля 2017

Вы можете написать подпрограмму для разных рангов массива и создать интерфейс. Здесь в примере я показал, как заполнить массив из другого массива с помощью оператора интерфейса `

program main 
    use data 
    implicit none 
    real,dimension(:,:,:),allocatable::data 
    integer::nx,ny,nz
    nx = 5
    ny = 10
    nz = 7
    call populate(nx,ny,nz,data)
    print *,data
end program main `

модуль данных здесь1004 *

module data  
  private 
  public::populate
  interface populate 
      module procedure populate_1d 
      module procedure populate_2d 
      module procedure populate_3d 
  end interface
 contains 
   subroutine populate_1d(x,data)
       implicit none 
       integer,intent(in)::x
       real,dimension(:),allocatable,intent(out):: data
       allocate(data(x))
       data=rand()
   end subroutine populate_1d 
   subroutine populate_2d(x,y,data)
       implicit none 
       integer,intent(in)::x,y
       real,dimension(:,:),allocatable,intent(out):: data
       allocate(data(x,y))
       data=rand()
   end subroutine populate_2d 
   subroutine populate_3d(x,y,z,data)
       implicit none 
       integer,intent(in)::x,y,z
       real,dimension(:,:,:),allocatable,intent(out):: data
       allocate(data(x,y,z))
       data=rand()
   end subroutine populate_3d 
end module data 

Существует интерфейс для заполнения 1d, 2d и 3d массивов.Вы можете вызвать интерфейс заполнения вместо вызова отдельных подпрограмм.Он автоматически выберет соответствующий.

...