Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart way to pass arguments in Fortran 90

I am a Fortran novice. I am trying to write a subroutine that will take in four arguments from the main program, and then outputs to the main program an array that involves the four arguments that were originally passed in. What is a good/smart way to do this?

For example, in my test program below, I create four real variables (a, b, c, and d) in the main program. Then I pass these real variables to a subroutine called mysub. I would like mysub to be able to take in a, b, c, and d, use them to populate a 2-by-2 array called o, and then send o to the main program for displaying (and possible modification) there. So, I tried the following:

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

But, I get the following error:

test.f90:10.53:

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

I interpret this as, the compiler doesn't know what o is, because o is not in the list of arguments in the subroutine header: SUBROUTINE mysub(w,x,y,z). So I probably need to include o in that header. So, I next try the following (where I have denoted changes or additions using !...):

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

This seems to work fine, and I get the correct output:

   1.1000000       2.2000000
   3.3000000       4.4000001

But, my question is, is this a good way to do this? In this working example, I'm declaring the array o both in the subroutine and in the main program. This seems potentially confusing, because I think that this means that I need to take care that either the subroutine or the main program allocates o (but not both, I think, in order to avoid error messages). Is there a smarter way to do this--to send an array from a subroutine to the main program? Thank you for your time.

like image 792
Andrew Avatar asked Aug 03 '11 19:08

Andrew


2 Answers

Your solution, making "o" an intent(out) argument is just fine. Without "o" being an argument there was no connection between the variable "o" in the subroutine and the variable "o" in the main program, and therefore there was no declaration or allocation of the one in the main program. Yet another solution (besides the one provided by @ja72) would be to alter your method: make "o" an intent(inout) argument of the subroutine and allocate it in the main program. Possible advantage: the allocate and deallocate are closer together in the code and paired. Possible disadvantage: depending on the the program logic and design, the array dimensions might be best known to the subroutine.

P.S. If you allocate the array in the main program, and don't actually use allocatable properties of the array in the subroutine (i.e., you don't allocate or deallocate it), then you don't have to declare it with the allocatable attribute in the subroutine -- a useful simplification. In which case "intent (out)" might be appropriate. But if you allocate the array in the main program and wish to pass that status to a subroutine, then the argument status can't be "intent (out)". "intent (out)" automatically deallocates the argument upon entry to the procedure.

like image 136
M. S. B. Avatar answered Sep 27 '22 19:09

M. S. B.


If you want to return an array you can a) add it to the arguments with INTENT(OUT) like your example #2 with allocation within the sub, or b) create a function and allocate the array externally:

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

Actually I think your solution is cleaner.

like image 42
John Alexiou Avatar answered Sep 27 '22 17:09

John Alexiou