How do you check values in Fortran like in Matlab? For example in the little program under, why does it show c=0 in main when it is c=36 in the subroutine testing? How do you make it so c=36 in the main program?
Can you call on the value c in some sort of way? I understand that in the main program the variable c is either undefined or has value 0, but is there a way to save the value of c in the subroutine so you can use it again in other subroutines, without calculating it again?
When the program is quite large it is handy to check values as you go.
program main
use test
implicit none
integer :: a,b,c
call testing(a,b)
write(*,*)'Test of c in main program',c
end program main
module test
implicit none
contains
subroutine testing(a,b)
integer :: a,b,c
a=2
b=3
c=(a*b)**a
write(*,*)'Value of c in subroutine',c
end subroutine testing
end module test
It is important to learn about scope in programming languages.
Every name (identifier) has only a limited range of validity.
If you declare some variable inside a subroutine
subroutine s
integer :: i
end subroutine
than the i is valid only in that subroutine.
If you declare a variable in a module
module m
integer :: i
contains
subroutines and functions
end module
then the i is valid inside all those subroutines and functions and also in all program units that use that module.
However, that does not mean that you should declare the variable in the module and just share it. That would be more or less a global variable. This is reserved only for certain cases, where that is necessary, but not for getting results out of your subprograms.
If your subroutine just computes something and you want to get the result of that computation you have two possibilities:
1. pass it as an additional argument, which will be defined by the subroutine
subroutine testing(a, b, c)
integer, intent(in) :: a, b
integer, intent(out) :: c
c = (a*b) ** a
end subroutine
you then call it as
call testing(2, 3, c)
print *, "result is:" c
2. Make it a function
function testing(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = (a*b) ** a
end function
and then you can use directly
c = testing(2, 3)
print *, "result is:", c
or just
print *, "result is:", testing(2, 3)
This is the desired behaviour. Basically the calling routine should know nothing about the subroutine except for how to call it and what to get back. This is called encapsulation.
You can make variables accessible to the calling routine by declaring it in the module itself, and not in the subroutine:
module test
implicit none
integer :: c
contains
subroutine testing(a, b)
implicit none
integer :: a, b
a = 2
b = 3
c = (a*b) ** a
write(*, *) "Value of c in the subroutine: ", c
end subroutine testing
end module test
program main
use test
implicit none
integer :: a, b
call testing(a, b)
write(*, *) "Test of c in the main program: ", c
end program main
Note that you must not declare c in the main program, as it will get the variable from the module.
You could also use a COMMON block, but modules are far superior.
Related
Let me just preface this by saying that I am very new to Fortran but familiar with Python. My research project however requires me to use some pre-written Fortran code, which at this moment wont compile on my pc. I am trying to understand why.
The actual code I am trying to compile is very long and probably not very illuminating, but I think I have managed to come up with a minimal example of where I think the issue is. Let's say I have a very simple module and subroutine as follows,
module arraycheck
implicit none
real*8, allocatable, dimension(:) :: x
contains
subroutine fillx
real*8, allocatable, dimension(:) :: x
allocate(x(5))
x(1) = 1
x(2) = 2
x(3) = 3
x(4) = 4
x(5) = 5
print*, x
end subroutine fillx
end module arraycheck
which I expect to simply create an array x, which when the subroutine fillx is called fills the array with the integers 1 to 5. My actual source contains something conceptually similar to this. Now I also have a main program as follows,
program main
use arraycheck
print*, x
call fillx
print*,x
end
My idea here would be that on the first print statement the variable x is still unallocated, so print returns nothing, and then on the second print statement x has been filled, so it should return the filled array.
However on both print statements nothing is returned. Now in my original source code something similar happens, which causes runtime to throw an error that an unallocated array was passed somewhere as an actual argument, which should have been allocated. It seems like the exact same thing happens as in my small example here.
So my question is, is the behaviour that I observe in my example here expected? And if it is, how can I alter the code to make it work in the way that I would want it to? If I know this I might better understand why the actual source doesn't work.
Just in case it is relevant, I am using gfortran on ubuntu.
Thanks!
You have too different xs. They do not have anything in common. One a module array and one array local to the subroutine. When you allocate the local array in the subroutine, it does not do anything to the other array in the module.
Also, you cannot print array that is not allocated. That is not standard conforming (undefined behaviour). Anything can happen. You should definitely enable all compiler checks when diagnosing problems. The compiler should complain about your code with those checks.
Remove the local array declaration and avoid referencing unallocated variables. Module procedures have access to module variables through host association.
module arraycheck
implicit none
real*8, allocatable, dimension(:) :: x
contains
subroutine fillx
allocate(x(5))
x(1) = 1
x(2) = 2
x(3) = 3
x(4) = 4
x(5) = 5
print*, x
end subroutine fillx
end module arraycheck
program main
use arraycheck
call fillx
print*,x
end
Also, real*8 is not standard Fortran, it is a non-standard extension. Fortran 90 and later uses the kind system instead.
Here are some other things which might be helpful - shown in UPPERCASE.
module arraycheck
USE ISO_C_BINDING, ONLY : C_Int32_t, C_DOUBLE
implicit none
PRIVATE
real(KIND=C_DOUBLE), allocatable, dimension(:), PUBLIC :: x
PUBLIC Creation_X, Destruction_X, FillX
contains
subroutine Creation_X(n)
USE ISO_C_BINDING, ONLY : C_Int32_t
IMPLICIT NONE
INTEGER(KIND=C_Int32_t), INTENT(IN) :: n
allocate(x(n))
RETURN
end subroutine Creation_X
subroutine Destruction_X
USE ISO_C_BINDING, ONLY : C_Int32_t
IMPLICIT NONE
IF(ALLOCATED(X)) DEALLOCATE(X)
RETURN
end subroutine Destruction_X
subroutine fillx
USE ISO_C_BINDING, ONLY : C_Int32_t
IMPLICIT NONE
INTEGER(KIND=C_Int32_t) :: N
DO I= 1, SIZE(x)
x(I) = I
ENDDO
RETURN
end subroutine fillx
end module arraycheck
program main
use arraycheck
CALL Creation_X(5)
call fillx
print*,x
CALL Destruction_X
end
I am a low level Fortran programmer. I am trying to make subroutines as generic as possible and I wonder if I can send an information of which subroutine another subroutine should acces from a given information in the main program. For instance, if I have:
program main
use test
implicit none
real(8) X
end program main
And:
module test
contains
subroutine A (X)
real(8) X
X = 2*X
end subroutine A
subroutine B (X)
real(8) X
X = 3*X
end subroutine B
end module test
I would like to, given the subroutine name 'A' or 'B' in the main program, transfer this to a third subroutine C inside the module test, which in turn would make some calculations and use the transferred name to choose between A and B in its calculations.
I know if I build the calculations of that subroutine C inside the main program, I can use a procedure pointer to access either A or B subroutines. However, I need to have the subroutine C. So I guess I will have a subroutine C with built in procedure pointer and it will take the name given in the main program as an argument. But I do not know how to do that. Neither if it is possible. If it is not, is there any other way? I do not know, maybe the subroutine C reading a txt file associating the read name to the procedure pointer. But, how?
Thank you, in advance!
I think that what you want is this: you first define subroutines A and B
module ab_m
implicit none
contains
subroutine A(X)
real, intent(inout) :: X
X = 2 * X
end subroutine A
subroutine B(X)
real, intent(inout) :: X
X = 3 * X
end subroutine B
end module ab_m
Then subroutine C uses a dummy procedure, specified by an interface,
module c_m
implicit none
contains
subroutine C(sub,y)
interface
subroutine sub(p)
implicit none
real, intent(inout) :: p
end subroutine sub
end interface
real, intent(inout) :: y
y = y + 1
call sub(y)
end subroutine C
end module c_m
and the main program chooses what procedure to use in C:
program p
use ab_m
use c_m
implicit none
real :: z(2)
z = 1.0
call C(B, z(1))
call C(A, z(2))
write(*,*) z ! writes 6.0, 4.0 [ (1+1)*3, (1+1)*2) ]
end program p
I'm sure the solution to this is extremely basic, but I'm having a hard time figuring out how to use functions in Fortran. I have the following simple program:
PROGRAM main
IMPLICIT NONE
INTEGER :: a,b
a = 3
b = 5
PRINT *,funct(a,b)
END PROGRAM
FUNCTION funct(a,b)
IMPLICIT NONE
INTEGER :: funct
INTEGER :: a,b
funct = a + b
END FUNCTION
I've tried several variations of this, including assigning a data type before FUNCTION, assigning the result of funct to another variable in the main program and printing that variable, and moving the FUNCTION block above the PROGRAM block. None of these worked. With the current program I get an error on line 6 (the line with the PRINT statement):
Error: Return type mismatch of function 'funct' (UNKNOWN/INTEGER(4))
Error: Function 'funct' has no IMPLICIT type
From all of the guides I've tried, I seem to be doing it right; at least one of the variations, or a combination of some of them, should have worked. How do I need to change this code to use the function?
Simply putting the function in the file will not make it accessible to the main program.
Traditionally, you could simply declare a function as external and the compiler would simply expect to find a suitable declaration at compile-time.
Modern Fortran organizes code and data in "modules". For your purpose, however, it is simpler to "contain" the function within the scope of the main program as follows:
PROGRAM main
IMPLICIT NONE
INTEGER :: a,b
a = 3
b = 5
PRINT *,funct(a,b)
CONTAINS
FUNCTION funct(a,b)
IMPLICIT NONE
INTEGER :: funct
INTEGER :: a,b
funct = a + b
END FUNCTION funct
END PROGRAM main
A simpler solution can be the following code
PROGRAM main
IMPLICIT NONE
INTEGER :: a,b, funct
a = 3
b = 5
PRINT *,funct(a,b)
END PROGRAM
FUNCTION funct(a,b)
IMPLICIT NONE
INTEGER :: funct
INTEGER :: a,b
funct = a + b
END FUNCTION
where the only difference is in the third line, where I have declared funct as an integer. It compiles and it prints 8 as result.
I write a simple demonstration code to present my question in a quick way. Here's the code, which can not be successfully built.
Main.f90
PROGRAM test
IMPLICIT NONE
INTEGER :: a
a = 1
CALL sub(a)
END PROGRAM
sub.f90
SUBROUTINE sub(a)
IMPLICIT NONE
INTEGER :: a
SELECT CASE(a)
CASE(1)
INTEGER :: b,c
b = a
c = a*2
CASE(2)
INTEGER :: b(4),c(4)
b(:) = a
c(:) = a*2
END SELECT
END SUBROUTINE
I tried to compile, but the error shows 'Unexpected data declaration statement' occurs in the subroutine file. Does it mean that I cannot declare argument type inside SELECT CASE structure? The problem is that I want to define the value of a in the main program and pass it into subroutine sub(a). The argument type of b and c should be decided by a, thus I cannot determine in advance. I also want to pass the value of b and c back to the main program, which I don't know how to do that. So how can I achieve this? Thanks.
So you you are actually asking how to return scalar or array from some subroutine, not how to declare construct-local variables. In that case consider using two separate subroutines. One version for scalars and one for arrays. You can overload them as a generic procedure under one name if you want.
Also think about ELEMENTAL, but if you use scalar a it won't work with the arrays.
If you still want to know how to declare local variables:
Variables can only be declared at the beginning of the procedure or at the beginning of a block. That is a Fortran 2008 feature supported in recent versions of the most common compilers (from PC compilers at least GNU and Intel).
SELECT CASE(a)
CASE(1)
BLOCK
INTEGER :: b,c
b = a
c = a*2
END BLOCK
The code as you write it is illegal, as you found out. Now some people have pointed to the 2008 feature of BLOCK statements, and if that's what you need, you can try that. But I'd like to learn more about what you want to do with this.
The very fact that you give them the same name suggests to me that you want to treat them the same way later on, which makes things really tricky.
Here are a few alternatives:
1) Use separate variables:
INTEGER :: b_scalar, c_scalar, b_array(4), c_array(4)
select case(a)
case(1)
b_scalar = a
c_scalar = 2*b_scalar
case(2)
b_array = a
c_array = 2*b_array
end select
2) Use allocatable arrays:
integer, dimension(:), allocatable :: b, c
select case(a)
case(1)
allocate(b(1), c(1))
case(2)
allocate(b(4), c(4))
end select
b = a
c = 2 * b
Now you have to remember that b and c are arrays, possibly with length 1. You have to treat them that way.
All of these have advantages and disadvantages. Without knowing why you are doing what you're doing, I don't really know how to best advise you.
As to your second question: The simple way to return them is as an INTENT(OUT) dummy argument. Here's a working example:
module mod_allocatable
contains
subroutine my_sub(a, b, c)
implicit none
integer, intent(in) :: a
integer, dimension(:), allocatable, intent(out) :: b, c
if (allocated(b)) deallocate(b)
if (allocated(c)) deallocate(c)
select case(a)
case(1)
allocate(b(1), c(1))
case(2)
allocate(b(4), c(4))
end select
b = a
c = 2 * b
end subroutine my_sub
end module mod_allocatable
program test_alloc
use mod_allocatable
implicit none
integer :: a
integer, allocatable, dimension(:) :: b, c
a = 1
call my_sub(a, b, c)
print *, "b is ", b
print *, "c is ", c
end program test_alloc
This is not overly elegant...
SUBROUTINE sub(a)
IMPLICIT NONE
INTEGER, INTENT(IN) :: a
INTEGER, DIMENSION(:), ALLOCATABLE :: b, c
SELECT CASE(a)
CASE(1)
IF(ALLOCATED(B)) THEN
IF(UBOUND(B)) .NE. 1) THEN
DEALLOCATE(B)
ALLOCATE(B(1))
ENDIF
ELSE
ALLOCATE(B(1))
ENDIF
IF(ALLOCATED(C)) THEN
IF(UBOUND(C)) .NE. 1) THEN
DEALLOCATE(c)
ALLOCATE(C(1))
ENDIF
ELSE
ALLOCATE(C(1))
ENDIF
b = a
c = a*2
CASE(2)
IF(ALLOCATED(B)) THEN
IF(UBOUND(B)) .NE. 4) THEN
DEALLOCATE(B)
ALLOCATE(B(4))
ENDIF
ELSE
ALLOCATE(B(4))
ENDIF
IF(ALLOCATED(C)) THEN
IF(UBOUND(C)) .NE. 4) THEN
DEALLOCATE(C)
ALLOCATE(C(4))
ENDIF
ELSE
ALLOCATE(C(4))
ENDIF
b(:) = a
c(:) = a*2
CASE(DEFAULT)
WRITE(*,*)'how did we get here?... a=',a
END SELECT
END SUBROUTINE Sub
Is there a way to check whether aliasing occurs in a Fortran subroutine, or at least to tell the compiler to issue a warning?
Consider this (rather simplistic) example:
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine
end module
program test
use alias
integer :: a, b
a = 1 ; b = 2
call myAdd(a, b, b)
print *, b, 'is not 3'
end program
Here, the result is set to zero in the subroutine. If the same variable is given as input and output, the result is (obviously) wrong. Is there a way to capture this kind of aliasing at run-time or at compile-time?
Yes, gfortran will detect some aliasing with the compiler option -Waliasing, however, the arguments must have intents in and out. It won't work with your example because you have declared argument c as intent(inout). In this example you can simply change the intent to out since the input value of c is not used. They try the compiler option! gfortran outputs:
alias.f90:17.16:
call myAdd(a, b, b)
1
Warning: Same actual argument associated with INTENT(IN) argument 'b' and INTENT(OUT) argument 'c' at (1)
I don't know of options in g95 or gfortran to detect the aliasing error at compile or run time. The programmer is responsible for avoiding such an error. You can pass the intent(in) arguments as expressions to ensure that they are not changed within the subroutine, as shown below.
module alias
contains
subroutine myAdd(a, b, c)
integer,intent(in) :: a, b
integer,intent(inout) :: c
c = 0
c = a + b
end subroutine myadd
end module alias
program test
use alias, only: myadd
integer :: a, b
a = 1 ; b = 2
call myAdd((a),(b),b) ! parentheses around arguments a and b
print*, b,"is 3"
call myAdd(a, b, b)
print*, b,"is not 3"
end program test