Умный способ передать аргументы в Фортран 90 - PullRequest
3 голосов
/ 03 августа 2011

Я новичок в Фортране.Я пытаюсь написать подпрограмму, которая будет принимать четыре аргумента из основной программы, а затем выводит в основную программу массив, который включает четыре аргумента, которые были первоначально переданы. Что такое хороший / умный способ сделать это?

Например, в моей тестовой программе ниже я создаю четыре действительные переменные (a, b, c и d) в основной программе.Затем я передаю эти реальные переменные подпрограмме с именем mysub.Я бы хотел, чтобы mysub мог принимать a, b, c и d, использовать их для заполнения массива 2 на 2, называемого o, и затем отправлять o к основной программе для отображения (и возможной модификации) там.Итак, я попробовал следующее:

SUBROUTINE mysub(w,x,y,z)
  IMPLICIT NONE
  REAL, INTENT(IN) :: w, x, y, z
  REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o

  ALLOCATE(o(2,2))
  o(1,1)=w
  o(1,2)=x
  o(2,1)=y
  o(2,2)=z
END SUBROUTINE mysub
END MODULE testsubs

PROGRAM test
  USE testsubs
  IMPLICIT NONE
  REAL :: a=1.1, b=2.2, c=3.3, d=4.4

  CALL mysub(a, b, c, d)

  PRINT *, o(1,1), o(1,2)
  PRINT *, o(2,1), o(2,2)
END PROGRAM test

Но я получаю следующую ошибку:

test.f90:10.53:

  REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o
                                                     1
Error: Symbol at (1) is not a DUMMY variable

Я интерпретирую это как, компилятор не знает, что такое o,потому что o нет в списке аргументов в заголовке подпрограммы: SUBROUTINE mysub(w,x,y,z).Поэтому мне, вероятно, нужно включить o в этот заголовок.Итак, я затем попробую следующее (где я обозначил изменения или дополнения, используя !...):

SUBROUTINE mysub(w,x,y,z,o) !...
  IMPLICIT NONE
  REAL, INTENT(IN) :: w, x, y, z
  REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: o

  ALLOCATE(o(2,2))
  o(1,1)=w
  o(1,2)=x
  o(2,1)=y
  o(2,2)=z
END SUBROUTINE mysub
END MODULE testsubs

PROGRAM test
  USE testsubs
  IMPLICIT NONE
  REAL :: a=1.1, b=2.2, c=3.3, d=4.4
  REAL, DIMENSION(:,:), ALLOCATABLE :: o !...

  CALL mysub(a, b, c, d, o) !...

  PRINT *, o(1,1), o(1,2)
  PRINT *, o(2,1), o(2,2)
  DEALLOCATE(o) !...
END PROGRAM test

Кажется, что это работает нормально, и я получаю правильный вывод:

   1.1000000       2.2000000
   3.3000000       4.4000001

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

Ответы [ 3 ]

3 голосов
/ 04 августа 2011

Ваше решение, указав "o" в качестве аргумента намерения (out), вполне подойдет. Без аргумента «o» не было никакой связи между переменной «o» в подпрограмме и переменной «o» в основной программе, и, следовательно, не было объявления или выделения переменной в основной программе. Еще одно решение (помимо предоставленного @ ja72) - изменить ваш метод: сделать «o» аргументом намерения (inout) подпрограммы и выделить его в основной программе. Возможное преимущество: allocate и deallocate находятся ближе друг к другу в коде и спарены. Возможный недостаток: в зависимости от логики и структуры программы, размеры массива могут быть наиболее известны подпрограмме.

P.S. Если вы выделяете массив в основной программе и фактически не используете выделяемые свойства массива в подпрограмме (т.е. вы не выделяете или не освобождаете его), то вам не нужно объявлять его с помощью атрибута allocatable в подпрограмме - полезное упрощение. В этом случае «намерение (из)» может быть уместным. Но если вы размещаете массив в основной программе и хотите передать этот статус подпрограмме, тогда аргумент status не может быть «намеренным (выходным)». «intent (out)» автоматически освобождает аргумент при входе в процедуру.

1 голос
/ 04 августа 2011

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

Как бы я это сделал:

  • , если размер известен во время компиляции: Объявите ваш массив восновная программа, используйте intent(out) в подпрограмме.

  • , если размер известен только во время выполнения: Выделите в основной программе, используйте intent(out) вподпрограмма и освобождение в основной программе.

Функции лучше всего подходят для небольших выходов, так как выходы должны быть скопированы.

1 голос
/ 04 августа 2011

Если вы хотите вернуть массив, вы можете а) добавить его к аргументам с помощью INTENT(OUT), как в вашем примере # 2 с размещением в подпрограмме, или б) создать функцию и распределить массив извне:

FUNCTION myfun(w,x,y,z,n,m)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n,m
REAL, DIMENSION(n,m) :: myfun
REAL, INTENT(IN) :: w,x,y,z

  myfun(1,1)=w
  myfun(1,2)=x
  myfun(2,1)=y
  myfun(2,2)=z

END FUNCTION
END MODULE testsubs

PROGRAM test
  USE testsubs
  IMPLICIT NONE
  REAL :: a=1.1, b=2.2, c=3.3, d=4.4
  REAL, DIMENSION(:,:), ALLOCATABLE :: o !...

  ALLOCATE(o(2,2))
  o  = myfun(a,b,c,d,2,2)

  PRINT *, o(1,1), o(1,2)
  PRINT *, o(2,1), o(2,2)
  DEALLOCATE(o) !...
END PROGRAM test

На самом деле я думаю, что ваше решение чище.

...