Using varibles in subroutines without passing them in Fortran [duplicate] - fortran

This question already has answers here:
Host Association vs Use Association in Fortran
(1 answer)
What are the ways to pass a set of variable values through the subroutine to a function without common block?
(3 answers)
Closed 1 year ago.
I start to work on an older Fortran code that uses heavily the following "feature":
subroutine sample
integer*4 :: a, b
a = 1
b = 1
call increment(b)
print *, a, b ! Prints "2 2"
contains
subroutine increment(c)
implicit none
integer*4, intent(inout) :: c
c = c + 1
a = a + 1
end subroutine increment
end subroutine sample
On multiple occasions in the code, a variable from a subroutine is used in (sub)subroutine without passing it through arguments. Before someone points it out, I don't think this is a good coding paradigm, but it is a code I should work on, and I have to deal with it.
My problem is I cannot find in the Fortran language reference if this is actually legal. I never used this "feature" I thought it is not legal but this code works for 20 years compiled with GCC, intel Fortran, pgi, nvhpc without a problem. Very recently I tried to compile it with one-API, but on multiple occasions, the variables in (sub)subroutine contained rubbish data (but not all the time).
My question is if this is actually a legal thing to do? Is it only a part of other compilers dialects? Or is it an oneapi's bug?

Related

forall index an associating entity?

I tried to compile this program with two different versions of gfortran:
program main
integer, dimension(1:2) :: iii
real, dimension(1:4,1:4,1:2) :: myArray
associate(iix => iii(1), iiy=> iii(2) )
forall( iix=1:4, iiy=1:4 )
myArray(iix,iiy,1) = iix + iiy
myArray(iix,iiy,2) = (iix + iiy)*10
endforall
end associate
print *, myArray(:,:,1)
print *, myArray(:,:,2)
end program main
There is no problem with GNU Fortran (GCC) 12.1.1 20220507 (Red Hat 12.1.1-1) version
But with GNU Fortran (Debian 10.2.1-6) 10.2.1 20210110 version, I get a compilation error
7 | forall( iix=1:4, iiy=1:4 )
| 1 internal compiler error: Segmentation fault
This version is not so old (2021/01/10)
From which gfortran version, is it possible to use associate in a forall statement?
And does this program conform to the standard?
It works with do loops
integer, dimension(1:2) :: iii
real, dimension(1:4,1:4,1:2) :: myArray
associate(iix => iii(1), iiy=> iii(2) )
do iix = 1,4
do iiy = 1,4
myArray(iix,iiy,1) = iix + iiy
myArray(iix,iiy,2) = (iix + iiy)*10
enddo
enddo
end associate
print *, myArray(:,:,1)
print *, myArray(:,:,2)
end program main
Following the comment of Jonathan Wakely, I sum up. In my initial code, there is also a do loop between the associate line and the forall line.
program main
integer, dimension(1:2) :: iii
real, dimension(1:4,1:4,1:2) :: myArray
integer :: i
myArray = 0.0
associate( iix => iii(1), iiy => iii(2) )
do i=1,2
forall( iix=1:4, iiy=1:4 )
myArray(iix,iiy,i) = (iix+iiy)*10*i
endforall
enddo
end associate
print *, myArray(:,:,1)
print *, myArray(:,:,2)
end program main
In comments we've addressed that the failure to compile with GCC 10 is a compiler bug which has been fixed in/by GCC 12. You offered to report this bug, but Jonathan Wakely has identified where this was fixed: there's little point reporting.
That said, there's still something we can say about code validity and workarounds.
Actually, I won't comment on validity, because what I'll say about the code itself makes that concept redundant.
Based on previous questions you've asked, you're using the ASSOCIATE with the FORALL to allow you to use an array element as the index. Something like
integer :: i(2)
forall (i(1)=1:4) ...
isn't allowed, so like with DO constructs and arrays you're associating a scalar variable with the element.
With FORALLs, though, that's entirely unhelpful.
The scope of an index in a FORALL statement/construct is that statement/construct. There's no connection between the array iii (in the question example) outside the FORALL and inside. There's simply no point associating the arrays elements with scalars to use those scalars in the FORALL.1
Note that you can2 "create" a new name specific to the FORALL itself, so you don't need to re-use names:
forall (integer :: iix=1:4, iiy=1:4, iiz=1:4) ...
instead of
integer iii(3)
associate (iix=>iii(1), iiy=>iii(2), iiz=>iii(3)
forall (iix=1:4, iiy=1:4, iiz=1:4) ...
end associate
FORALLs and DOs are very different things.
Some Fortran developers would say that the appropriate workaround for GCC 10 is to simply avoid using FORALLs at all. FORALLs are obsolescent in the Fortran standard and may be removed in the future. It's a reasonable position to hold that they should be avoided in new code.
1 If you don't have compiler support for what I mention next, then there is a (weak) point in using an ASSOCIATE to tidy up the number of declared scalar variables - in this case use a BLOCK construct to at least keep them local. It's hard to see the ASSOCIATE example above as any tidier than
block
integer iix, iiy, iiz
forall (iix=1:4, iiy=1:4, iiz1:4) ...
end block
Or avoid the obsolescent FORALL altogether.
2 If you have compiler support for this Fortran 2008 feature. GCC at the time of answering is missing this support.

Moment of inertia in fortran [duplicate]

This question already has answers here:
Modern Fortran equivalent of an action statement shared by nested DO and GO TO
(1 answer)
Function has no implicit type
(4 answers)
Closed 1 year ago.
I am trying to calculate the moment of inertia in fortran. The formula I am using is following: The code I am using:
program moment
implicit none
real :: cR,h,rho0,a,b,c,d,resultV,pi,resultMI,aMass,exactresMI,exactresV,r,res,z,rho
integer :: m,n
! rho0 = density, cR=capital R( radius),h= height )
rho0=10000
cR=0.05
h=0.1
a=0.d0
b=h
c=0.d0
d=cR
m=1000
n=1000
call cheb2(a,b,m,c,d,n,funV,res)
pi=4*datan(1.d0)
resultV=res*2*pi
exactresV= pi/3*cR**2*h
write(*,*)
write(*,*) "Numerical volume result =", resultV
write(*,*) "Exact volume result = ",exactresV
call cheb2(a,b,m,c,d,n,funV,res)
resultMI=res*2*pi
aMass=exactresV*rho0
exactresMI=3/10.*aMass*cR**2
write(*,*)
write(*,*) "Numerical Moment of Inertia result =", resultMI
write(*,*) "Exact Moment of Inertia result = ",exactresMI
end program
function funV(z,r)
if (r.gt.z*cR/h) then
rho=0.d0
else
rho=1.d0
end if
funV=rho*r
return
end
function funMI(z,r)
if (r.gt.z*cR/h) then
rho=rho0
else
rho=1.d0
endif
funMI=rho*r**3
return
end
include "CHEB.FOR"
Our instructor does not use "implicit none" , so I am really new to this operator. Out instructor gave us CHEB.FOR code for calculating 2 dimensional integrals. I am writing it here:
subroutine ch4xy(al,bl,cl,dl,f,ri)
implicit double precision (a-h,o-z)
common/ttxy/ t1,t2
dimension xx(4),yy(4)
c1=(al+bl)/2.d0
c2=(dl+cl)/2.d0
d1=(-al+bl)/2.d0
d2=(dl-cl)/2.d0
xx(1)=c1+d1*t1
xx(2)=c1+d1*t2
yy(1)=c2+d2*t1
yy(2)=c2+d2*t2
xx(3)=c1-d1*t1
xx(4)=c1-d1*t2
yy(3)=c2-d2*t1
yy(4)=c2-d2*t2
ss=0
do 3 i=1,4
do 3 j=1,4
ss=ss+f(xx(i),yy(j))
3 continue
ri=ss*d1*d2/4.d0
return
end
subroutine cheb2(a,b,m,c,d,n,f,r)
implicit double precision (a-h,o-z)
external f
common/ttxy/ t1,t2
t1=0.187592
t2=0.794654
hx=(b-a)/m
hy=(d-c)/n
rr=0
do 5 i=1,m
do 5 j=1,n
aa=a+(i-1)*hx
bb=aa+hx
cc=c+(j-1)*hy
dd=cc+hy
call ch4xy(aa,bb,cc,dd,f,ri)
rr=rr+ri
5 continue
r=rr
return
end
When I compile the file, a couple of errors and a warning appear:
CHEB.FOR:19:17:
19 | do 3 j=1,4
| 1
Warning: Fortran 2018 deleted feature: Shared DO termination label 3 at (1)
CHEB.FOR:36:11:
36 | do 5 j=1,n
| 1
Warning: Fortran 2018 deleted feature: Shared DO termination label 5 at (1)
momentOFinertia.f95:17:27:
17 | call cheb2(a,b,m,c,d,n,funV,res)
| 1
Error: Symbol 'funv' at (1) has no IMPLICIT type
First, I dont understand why funV is unclassifiable statement, it classifies as a function. Second, our instructor used some old operations which is apparently not valid in new fortran. I dont know what could replace "shared do".
The main problem (fixed by your edit)is that your code misses an end or end program at the end. Alternatively, you could put an end program after your functions and contains between the subroutine and the main program body. That would make the functions to be internal subprograms and would fix your other problem - no implicit type for the functions.
This - putting the functions inside the program as internal subprograms, allows the program to "see" them and then the program can correctly pass them to other procedures. Alternatively, you could make an interface block for them or declare their type and let them be external. See Why do I have to specify implicitly for a double precision return value of a function in Fortran? for more.
You also have a type mismatch. The code you got from your instructor uses double precision but your code uses the default real. You have to synchronize that. Update your code to double precision, either using double precision or using real kinds.
The compiler also warns you that your program is using deleted features. These features were deleted in modern revisions of the Fortran standards. However, the compiler remain largely backwards compatible and will compile the code including those features anyway, unless you request strictly a certain standard revision.
In this case two do-loops use one common continue statement
do 5 ...
do 5 ...
5 continue
This is not allowed and can be fixed by inserting another continue with another label or, better, by using end do.

Fortran Type mismatch in argument n at 1 passed REAL(8) to integer (4)

I'm having a problem with a fortran 77 project (Yes i know it's archaic, but my prof requres us to program in fixed form fortran)
So I'm having a problem with a subroutine, which should read an N dimension Vector, which should be a column. the code for this looks like: (its still an early draft for my homework, just trying to figure out how to call a subroutine the rest of the code will be done if i can compile this problem)
Program gauss
implicit double precision (A-H,O-Z)
!he directly asked for implicit typing
call Vread(V(N))
end program
Subroutine Vread(V,N)
Implicit double precision (A-H,O-Z)
dimension V(N)
read(*,*) (V(I),I=1,N)
return
end
So my problem is: If I try to compile it with gfortran gauss.exe -o gauss.f the compiler returns with error:
Type mismatch argument 'n' at(1); passed REAL(8) to Integer(4)
In your main program you write
call Vread(V(N)) ! this passes a rank-1 vector with N elements
but your subroutine is declared
Subroutine Vread(V,N) ! this requires 2 arguments
Change the call to
call Vread(V,N)
and let your professor know that the 21st Century arrived a while age. Note too that it is possible to write 21C Fortran in fixed-form. It doesn't make much sense, but might enable you to toe the line while developing in a more modern version of the language.
!he directly asked for implicit typing
I trust that you are in a jurisdiction where you do not pay directly for your education. If you are paying fees then demand more, you're being cheated.

Assignment to unallocated variable [duplicate]

This question already has answers here:
Automatic array allocation upon assignment in Fortran
(2 answers)
Closed 5 years ago.
A line in my code assigns an array to an unallocated array. I thought it was a bug but to my surprise it works just fine.
program test
implicit none
real, allocatable :: x(:,:)
real :: y(2,2)
y = 1.
x = y
print*, x
end program test
How does this work in terms of memory? The = operator here just allocates and assigns at the same time? Why is this possible and why is the compiler not complaining? I am using gfortran 5.4.0.
Since Fortran 2003, allocatable arrays will be automatically allocated (or re-allocated if the shape has changed) at run time. See for instance the Fortran 2003 features of the NAG compiler https://www.nag.com/nagware/np/r61_doc/nag_f2003.pdf or look for "realloc" in the documentation of gfortran https://gcc.gnu.org/onlinedocs/gfortran/Error-and-Warning-Options.html#Error-and-Warning-Options
That is correct, according to Section 7.2.1.3, "Interpretation of intrinsic assignments" of WD 1539-1, paragraph 3.

Have a function in fortran return a reference that can be placed on the left-hand-side of an assignment

As stated in the title, I want to directly modify data that I access through a pointer retrieved from a function. Having a reference returned by a function appearing on the l.h.s. of an assignment(=) is no issue in C++ but the following minimal example in fortran errors out:
module test_mod
implicit none
integer, target :: a=1, b=2, c=3 ! some member variables
contains
function get(i)
integer, pointer :: get
integer, intent(in) :: i
select case (i)
case (1)
get => a
case (2)
get => b
case (3)
get => c
end select
end function get
end module test_mod
program test
use test_mod
implicit none
integer, pointer :: i_p
!> prints out 1 2 3
print*, get(1), get(2), get(3)
!> this is what I want but I get the error
!> Error: 'get' at (1) is not a variable
get(2) = 5
!> this works but is not what I want
i_p => get(2)
i_p = 5
end program test
Is there any way to accomplish this behaviour; maybe I'm missing some attributes? I would like to bypass writing any setter routines such as
set(i,value)
since it should mimic the appearance of an array.
In my application, the member variables a,b,c are actually arrays of different size
a = [a1, a2, a3]
b = [b1, b2]
c = [c1]
and I want the getter get(i,j) to mimic a matrix of pointers
j = 1 2 3
i = 1: [[a1, a2, a3],
i = 2: [b1, b2, XX],
i = 3: [c1, XX, XX]]
wehre XX would be referencing to null().
Update:
I am using gfortran (version 5.2.0) and the deployment machines would have only versions starting from 4.6.x and upwards. Therefore, the suggested fortran 2008 standard features are unfortunately not available to me. Is it possible to mimic the behaviour described above without having a compiler supporting it out of the box?
Update 2:
So I ended up implementing a structure as follows
type Vec_t
integer, allocatable, dimension(:) :: vec
end type Vec_t
type(Vec_t), allocatable, dimension(:), target :: data
which I initialise like this (my triangular matrix application I mention at the end)
allocate(data(max))
do i=1,max
allocate(data(i)%vec(i))
end do
and I access & write to it through
print*, data(2)%vec(1)
data(2)%vec(1) = 5
which is not precisely what I was after but good enough for my application.
Let's look at what you want to do:
get(2)=5
and the error message
Error: 'get' at (1) is not a variable
That looks pretty comprehensive: you can't do what you want. Or, perhaps...
get(2) is indeed, under the rules of Fortran 2003, not a variable. In Fortran 2003 a variable is given by the rules R601 and R603, which is a list of designators.
The left-hand side of an assignment must be a variable.
But look at Fortran 2008 and its definition of a variable. Now a variable is either one of those same designators (or ones related to coarrays or complex parts), but it could also (C602 to R602) be a function reference which
shall have a data pointer result.
This is summarized in the introduction of Fortran 2008, detailing the extensions over Fortran 2003, as
A pointer function reference can denote a variable in any variable definition context.
get(2) is a reference to a function that has a data pointer result. get(2) then may appear on the left-hand side of an assignment statement under the rules of Fortran 2008.
Alas, this interpretation of Fortran is not widely supported by current compilers: at the time of answering just the Cray compiler.
This means that this answer is really saying that you have two options: switch compiler or wait until this feature is more widespread. As both of these are likely impractical, you probably want another answer which gives something slightly more portable as a workaround.
I prefer my link to that given by innoSPG, as although this latter is based on the former, the description of the appropriate field "Pointer functions - pointer function ref is a variable" is slightly more clear. This is, though, a more accessible document and a viable alternative.