This question already has an answer here:
Variable format
(1 answer)
Closed 2 years ago.
I used to the Intel fortran compiler which supports using the <n> extension like
write(*, '(<n>(2I4))') (i, 2*i, i=1,n)
To illustrate, I give a s1_fprint.f90 subroutine as follows
subroutine fprint(name,bb)
IMPLICIT NONE
character(len=*), intent(in) :: name
real, intent(in) :: bb(:,:)
integer :: column=10
integer i,j,k,m,n
n = size(bb,1)
m = size(bb,2)
write(*,'(1a)')name
do k=0,m/column-1
write(*, '(1x,<column>i16)')(i,i=k*column+1,(k+1)*column)
write(*,'(1i10,<column>f)')(i,(bb(i,j),j=k*column+1,(k+1)*column),i=1,n)
write(*,'(/)')
end do
if(mod(m,column)/=0)then
write(*, '(1x,<m-m/column*column>i16)')(i,i=m/column*column+1,m)
write(*,'(1i10,<m-m/column*column>f)')(i,(bb(i,j),j=m/column*column+1,m),i=1,n)
write(*,'(/)')
endif
end subroutine fprint
Now, I change the Intel fortran compiler to gfortran, then I test (t1_useSur.f90)
above subroutine in gfortran as follows:
program main
implicit none
real :: A(2,3) = reshape([1.2, 2.3, 3.4, 4.5, 5.6, 6.7], [2,3])
call fprint('A',A)
end program main
which should show us something like
A
1 2 3
1 1.2000000 3.4000001 5.5999999
2 2.3000000 4.5000000 6.6999998
However, in gfortran compiler when I run
gfortran t1_useSur.f90 s1_fprint.f90 -o out
./out
there are many errors as
.\s1_fprint.f90:14.17:
write(*, '(1x,<column>i16)')(i,i=k*column+1,(k+1)*column)
1
Error: Unexpected element '<' in format string at (1)
.\s1_fprint.f90:15.19:
write(*,'(1i10,<column>f)')(i,(bb(i,j),j=k*column+1,(k+1)*column),i=1,n)
1
Error: Unexpected element '<' in format string at (1)
.\s1_fprint.f90:19.17:
write(*, '(1x,<m-m/column*column>i16)')(i,i=m/column*column+1,m)
1
Error: Unexpected element '<' in format string at (1)
.\s1_fprint.f90:20.19:
write(*,'(1i10,<m-m/column*column>f)')(i,(bb(i,j),j=m/column*column+1,m),i=
1
Error: Unexpected element '<' in format string at (1)
Since the <n> extension is not supported by gfortran, how can fix these problems?
Near dupe Variable format statement when porting from Intel to GNU gfortran
For the 1-dim cases like (i,i=...) :
If you have (or get) a version of gfortran that supports F08, which browsing https://gcc.gnu.org/onlinedocs/ appears to be around 4.6.4 up, use * as the count like (1x,*i16)
Otherwise, use the old-as-the-hillsF77 trick: since format repetitions or items 'beyond' the data list are ignored, just use a repetition count that is at least as big as the data will ever be (but not more than HUGE(0)) here (1x,10i16) is actually enough but something like (1x,999i16) makes it much more obvious
or if you like extra work, do on-the-fly like the 2-dim case below
For the 2-dim cases like (i,(bb(i,j),j=...),i=...) which currently use format looping to insert record breaks either:
reduce to 1-dim by making the record breaks into separate WRITEs:
do i=...
write(*,'(1i10,*f)') i,(bb(i,j),j=...)
end do !i
generate the correct count on-the-fly:
character(len=20) fmt
...
write(fmt,'(a,i0,a)') '(1i10,', numcols_expression, 'f)'
write(*, trim(fmt)) (i,(bb(i,j),j=...),i=...)
... or ...
write(fmt,'(i0)') numcols_expression
write(*, '(1i10,'//trim(fmt)//'f)') (i,bb(i,j),j=...),i=...)
PS: you don't actually need 1i10 just i10, but I left it for consistency. Also rather than a loop for the full chunks then an if for the partial chunk, which have to be kept in sync, I would probably do:
do k=1,m,column
l=min(k+column-1,m)
... print chunk for i=k,l (numcols is l-k+1) ...
end do !k
Related
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.
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.
Here is the source (lets call it test4.F):
do 10 i=1
write(*,*) i
10 continue
end
With gfortran:
$gfortran test4.F
$./a.out
-259911288
with ifort:
$ifort test4.F
$./a.out
0
I know the syntax is incorrect. So both should throw compile-time errors.
Your test program is indeed not valid Fortran, but there isn't a syntax problem.
When the file has a .F suffix both gfortran and ifort assume that the program uses fixed form source. In fixed form source, blanks in statements aren't significant (outside character contexts). Your program is equivalent to:
do10i=1
write(*,*) i
10 continue
end
With comments:
do10i=1 ! Set the real variable do10i to value 1
write(*,*) i ! Write the undefined integer variable i
10 continue ! Do nothing
end ! End execution
Because of implicit typing you have a defaul real variable do10i and so there isn't a syntax error for a do control, and there isn't a do control that sets i to 1.
Instead your program is invalid because you are referencing the value of i although it hasn't first been defined (because it isn't a loop variable). But this isn't an error the compiler has to complain about at compile time (or even run time). A compiler is allowed to print any value it likes. gfortran chose one, ifort another.
Liberal use of implicit none and avoiding fixed-form source are good ways to avoid many programming errors. In this case, use of an end do instead of continue would also alert the compiler to the fact that you intended there to be some looping.
Which version of gfortran are you using? With gfortran-6, the following
PROGRAM MAIN
USE ISO_FORTRAN_ENV, ONLY:
1 COMPILER_VERSION,
2 COMPILER_OPTIONS
C EXPLICIT TYPING ONLY
IMPLICIT NONE
C VARIABLE DECLARATIONS
INTEGER I
DO 10 I = 1
WRITE (*,*) I
10 CONTINUE
PRINT '(/4A/)',
1 'THIS FILE WAS COMPILED USING ', COMPILER_VERSION(),
2 ' USING THE OPTIONS ', COMPILER_OPTIONS()
END PROGRAM MAIN
indeed throws the error
main.F:13:13:
DO 10 I = 1
1
Error: Symbol ‘do10i’ at (1) has no IMPLICIT type
I suspect that test4.F does not include the implicit none statement. Consequently, DO 10 I = 1 is interpreted as an implicitly defined real variable DO10I = 1. Here is a sample of a fixed form statement showing what are now (post Fortran 90) considered significant blanks followed by an equivalent statement without the blanks
DO I=1 , M AX ITER S
DO I = 1 , MAXITERS
Upshot, always use explicit variable declarations in all your programs. Better yet, only write Fortran in free source form main.f90. Free source form is more compatible with modern interactive input devices than fixed form. The maximum line length is 132 characters, compared to the older limit of 72 characters. This reduces the possibility of text exceeding the limit, which could lead the compiler to misinterpret names. Here's the same code in free source form
program main
use ISO_Fortran_env, only: &
stdout => OUTPUT_UNIT, &
compiler_version, &
compiler_options
! Explicit typing only
implicit none
! Variable declarations
integer :: i
do i = 1, 3
write (stdout, *) i
end do
print '(/4a/)', &
' This file was compiled using ', compiler_version(), &
' using the options ', compiler_options()
end program main
and
gfortran-6 -std=f2008ts -o main.exe main.f90
./main.exe
yields
1
2
3
This file was compiled using GCC version 6.1.1 20160802 using the options -mtune=generic -march=x86-64 -std=f2008ts
When I try to compile my code using gfortran 4.4.7 I get the following error message:
Error: Type mismatch in argument 'intkind8' at (1); passed INTEGER(4)
to INTEGER(8).
With ifort it does compile, unless I demand the F2003 standard, in which case a similar error is given.
My code:
program kindDummy
implicit none
call takeIntKind4And8(0,0)
contains
subroutine takeIntKind4And8(intKind4, intKind8)
implicit none
integer(kind=4), intent(in) :: intKind4
integer(kind=8), intent(in) :: intKind8
print *, 'Integer(kind4): ', intKind4
print *, 'Integer(kind8): ', intKind8
end subroutine takeIntKind4And8
end program kindDummy
I was wondering if there's an elegant way to make the compiler "turn" the first 0 into a kind=4 integer, and the second one into a kind=8?
In
call takeIntKind4And8(0,0)
both zeros have the default kind. The kind numbers are not portable, but your default one is probably 4.
To produce 0 of kind 8 use 0_8:
call takeIntKind4And8(0_4,0_8)
I recommend to stay away from using 4 and 8 directly and use integer constants like 0_ip where ip is an integer constant with the right value. See Fortran: integer*4 vs integer(4) vs integer(kind=4) for more.
I want to load a list from an input file via a namelist. To compile, I use gfortran:
PROGRAM main
IMPLICIT NONE
INTEGER :: val,err
NAMELIST /myNamelist/ val
OPEN(100,file='input.txt')
READ(unit=100,nml=myNamelist,iostat=err)
CLOSE(100)
PRINT *, val
END PROGRAM
The input.txt looks like the following:
&myNamelist
val = 3e3
/
The program prints a 0 instead of 3000. If I plug in val = 3000 in the input file, it works. It seems that gfortran does not support scientific notation in a namelist. With ifort however it runs fine. Is there a workaround or something to use scientific notation in a namelist with gfortran?
You use iostat=err, but you do not check the value of err! If you did that you would found out that an error condition happened and err is nonzero. Therefore, val is of no use.
The scientific notation is not valid for integer input. Either read a real variable or do not use the scientific notation.
Try:
PROGRAM main
IMPLICIT NONE
INTEGER :: val,err
character(256) :: msg
NAMELIST /myNamelist/ val
OPEN(100,file='input.txt')
READ(unit=100,nml=myNamelist,iostat=err,iomsg=msg)
CLOSE(100)
PRINT *, err
PRINT *, msg
print *, val
END PROGRAM
run:
> sunf90 intnml.f90
> ./a.out
1083
unexpected character in integer value
0
BTW, Intel Fortran accepts the value, but that is a non-standard extension. Your program would be non-portable if you relied on that.
The draft of the 2008 standard that I have to hand states, at para 10.11.3.3.6, in the context of reading name lists:
When the next effective item is of type integer, the value in the input
record is interpreted as if an Iw edit descriptor with a suitable
value of w were used.
In this case gfortran is doing no more than the standard requires while the Intel compiler goes a bit further and implements an extension that copes with a non-standard form of an integer value, ie 3e3.