I can't tell what is wrong with this free form Fortran program. It does not correctly handle its command line arguments.
It works if I use a static array for the command line argument instead of an allocatable array.
Also, is this a good first Fortran program? Is this the type of problem for which Fortran would be useful? I already know C, C++, and a little bit of D.
module fibonacci
use ISO_FORTRAN_ENV
implicit none
contains
subroutine output_fibonacci(ordinal)
! Declare variables
integer, parameter :: LongInt = selected_int_kind (38)
integer, intent(in) :: ordinal
integer :: count
! integer (kind=LongInt) :: count, compare=2
integer (kind=LongInt), dimension(2,2) :: matrix, initial
matrix=reshape((/ 1, 1, 1, 0 /), shape(matrix))
initial=reshape((/ 1, 0, 0, 1 /), shape(initial))
count = ordinal
! Do actual computations
do while (count > 0)
! If the exponent is odd, then the output matrix
! should be multiplied by the current base
if (mod(count,2) == 1) then
initial = matmul(matrix, initial)
end if
! This is the squaring step
matrix = matmul(matrix, matrix)
count = count/2
end do
write (*,*) initial(1,2)
end subroutine output_fibonacci
end module fibonacci
program main
use, intrinsic :: ISO_FORTRAN_ENV
use fibonacci
implicit none
! The maximum allowed input to the program
integer :: max=200, i, size=20
character, allocatable :: argumen(:)
integer :: error, length, input
allocate(argumen(size))
! write(*,*) argcount
do i=1, command_argument_count()
call get_command_argument(i, argumen, length, error)
read(argumen,*,iostat=error) input
! write(*,*) argument
! write (*,*) input
if (error .ne. 0) then
write(ERROR_UNIT,'(I36.1,A)') input, "is not an integer"
stop (1)
else if (input > max) then
write(ERROR_UNIT,'(A,I36.1,A)') "Input ", input, " is too large"
stop (1)
end if
call output_fibonacci(input)
end do
end program
This line
character, allocatable :: argumen(:)
declares an allocatable array of characters. So the statement
allocate(argumen(size))
makes argumen an array of 20 single-character elements. That's not the usual way of dealing with strings in Fortran and argumen doesn't match (in type or rank) the requirements for the second argument in the call to get_command_argument.
Instead you should write
character(len=:), allocatable :: argumen
to declare argumen to be a character variable of allocatable length. In some contexts you can simply assign to such a variable, e.g.
argumen = 'this is the argument'
without having to previously allocate it explicitly.
With Intel Fortran v14 the call to get_command_argument compiles without warning but on execution the argument argumen isn't automatically allocated and it remains unassigned. I'm honestly not sure if this behaviour is standard-conforming or not. One approach would be to make two calls to get_command_argument, first to get the size of the argument, then to get the argument; like this
do i=1, command_argument_count()
call get_command_argument(i, length=length, status=error)
allocate(character(length)::argumen)
call get_command_argument(i, argumen, status=error)
! do stuff with argument
deallocate(argumen)
end do
Using the name length for the variable to be assigned the value returned by the optional argument called length is legal but a mite confusing. The deallocate statement ensures that argumen can be allocated again for the next argument.
I'll leave you the exercise of declaring, and using, an allocatable array of allocatable-length characters.
Disclaimer: the next two paragraphs contain material which some may find subjective. I will not be entering into any discussion about these parts of this answer.
Is this a good first Fortran program ? It's better than a lot of what I see here on SO. Personally I prefer the consistent use of the modern /= to .ne., < to .lt (etc), I don't use stop if I can avoid it (I usually can), and I'm sure I could find other nits to pick.
Is this the type of problem for which Fortran would be useful? Fortran is useful for all types of problem, though I admit it can be quite challenging using it to write a web server.
Related
The following function is supposed to convert a C string into a Fortran string and works fine in Release builds, but not in Debug:
! Helper function to generate a Fortran string from a C char pointer
function get_string(c_pointer) result(f_string)
use, intrinsic :: iso_c_binding
implicit none
type(c_ptr), intent(in) :: c_pointer
character(len=:), allocatable :: f_string
integer(c_size_t) :: l_str
character(len=:), pointer :: f_ptr
interface
function c_strlen(str_ptr) bind ( C, name = "strlen" ) result(len)
use, intrinsic :: iso_c_binding
type(c_ptr), value :: str_ptr
integer(kind=c_size_t) :: len
end function c_strlen
end interface
l_str = c_strlen(c_pointer)
call c_f_pointer(c_pointer, f_ptr)
f_string = f_ptr(1:l_str)
end function get_string
However, it seems that c_f_pointer does not tell the Fortran pointer-to-string, f_ptr, the length of the string it is pointing to. In Debug builds, where bounds-checking is active, this results in
Fortran runtime error: Substring out of bounds: upper bound (35) of 'f_ptr' exceeds string length (0)
I'm using gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 and set the standard to 2008.
My question: Is there any way to tell the f_ptr its length without changing the declaration or am I doing something fundamentally the wrong way here?
It seems to run correctly if I specify the shape, but for that f_ptr needs to be an array:
character(len=:), allocatable :: f_string
character(len=1), dimension(:), pointer :: f_ptr
...
call c_f_pointer(c_pointer, f_ptr, [l_str])
However, I cannot find a way to transform that string of rank 1 to the character(len=:), allocatable :: f_string, which apparently has rank 0.
My second question: Is there any way to transfer the f_ptr data into the f_string in this example?
You cannot use c_f_pointer to set the length of the Fortran character pointer (F2018, 18.2.3.3):
FPTR shall be a pointer, shall not have a deferred type parameter [...]
A deferred-length character scalar (or array) therefore cannot be used (the length is a type parameter).
You can indeed use a deferred-shape character array as fptr and then use any number of techniques to copy the elements of that array to the scalar (which is rank-0 as noted).
For example, with substring assignment (after explicitly allocating the deferred-length scalar):
allocate (character(l_str) :: f_string)
do i=1,l_str
f_string(i:i)=fptr(i)
end
Or consider whether you can simply use the character array instead of making a copy to a scalar.
I try to introduce complex-valued array and variables in Fortran. For the following code
program
integer :: i , j !two dimensional real array
real(dp) :: m1(3,2)
complex(dp) :: a1(3,2),a2(3,2), c0, c1
!assigning some values to the array numbers
c0 = (1.0_dp, 0.0_dp)
c1 = (0.000000001_dp, 0.0_dp)
do i=1,3
do j = 1, 2
a1(i,j) = c0*i*j
end do
end do
do i=1,3
do j = 1, 2
a2(i,j) = c0*i*j + c1
end do
end do
do i=1,3
do j = 1, 2
if (dabs( dreal(a1(i,j)) - dreal(a2(i,j))) > 1.e-6 then
write (*,*), 'warning',i,j, a1(i,j), a2(i,j)
end if
end do
end do
write (*,*), a1(1,1), a2(1,1)
end program
ifort gives me
complex(dp) :: a1(3,2), a2(3,2)
-----------^
why complex(dp) requires compile-time constant and how to fix it? Thank you.
The kind parameter dp must be a constant, see Fortran - setting kind/precision of a variable at run time
However, you do not have the same problem as in the link, you did not try to define dp at all! First, you must use IMPLICIT NONE, it is absolutely necessary for safe programming and the biggest problem with your code. Then it will tell you that the type of dp is not declared.
You just define the kind constant in one of the usual ways, as a constant. The most simple is:
program main
!NECESSARY!
implicit none
integer, parameter :: dp = kind(1.d0)
Note that I named the program main, you have to name your program. Or just omit the program.
More about defining real kinds can be found at Fortran 90 kind parameter
Another note: Forget dabs() and dreal(). dabs() is an old remnant of Fortran 66 and dreal() is not standard Fortran at all. Just use abs and either dble() or better real( ,dp).
I'm modernizing some old Fortran code and I cannot get rid of an equivalence statement somewhere (long story short: it's mixed use is so convoluted it'd take too much work to convert everything).
I need the length of the EQUIVALENCEd arrays to depend on some input, like the following code:
program test_equivalence
implicit none
type :: t1
integer :: len = 10
end type t1
type(t1) :: o1
call eqv_int(o1%len)
call eqv(o1)
return
contains
subroutine eqv_int(len)
integer, intent(in) :: len
integer :: iwork(len*2)
real(8) :: rwork(len)
equivalence(iwork,rwork)
print *, 'LEN = ',len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv_int
subroutine eqv(o1)
type(t1), intent(in) :: o1
integer :: iwork(o1%len*2)
real(8) :: rwork(o1%len)
equivalence(iwork,rwork)
print *, 'LEN = ',o1%len
print *, 'SIZE(IWORK) = ',size(iwork)
print *, 'SIZE(RWORK) = ',size(rwork)
end subroutine eqv
end program test_equivalence
This program will create 0-length arrays with gfortran 9.2.0. This is the output:
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
LEN = 10
SIZE(IWORK) = 0
SIZE(RWORK) = 0
The same code will return Array 'rwork' at (1) with non-constant bounds cannot be an EQUIVALENCE object when compiled with gfortran 5.3.0, the warning disappears since gfortran 6.2.0, but the size of the arrays is always 0. So maybe compiler bug?
The source code is indeed not a valid Fortran program. To be specific, it violates the numbered constraint C8106 of Fortran 2018:
An equivalence-object shall not be a designator with a base object that is .. an automatic data object ..
Being a numbered constraint, the compiler must be capable of detecting this violation. If hasn't such a capability this is a deficiency in the compiler (a bug). Being "capable" doesn't mean doing so by default, so please look carefully to see whether there are options which do lead to this detection. Someone familiar with the internals of GCC can give further detail here.
As the source isn't a valid Fortran program, the compiler is allowed to consider the arrays of size zero if it has skipped the violation detection.
I was doing an svd decomposition of a square matrix A, with A=U S Vdag, and in the fortran code, the line reads
lwork = -1
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
lwork = int(work(1));deallocate(work); allocate(work(lwork))
call zgesvd( 'A', 'A', A%d, A%d, A%m, A%d, S, U%m, U%d, Vdag%m, Vdag%d,work, lwork, rwork, info )
When I compiled with gfortran, it went through with no error or warning. However when I run the program, it shows error with message:
" ** On entry to ZGESVD parameter number 11 had an illegal value "
I could not figure out what went wrong.
For reference, the definitions of the parameters:
type cmatrix
integer(4) d
complex(8), allocatable :: m(:,:)
end type
type (cmatrix) A,U,Vdag
allocate(A%m(dim,dim),U%m(dim,dim),Vdag%m(dim,dim))
A%d = dim; U%m = dim; Vdag%d = dim
real(8) S(dim)
Thanks in advance!
Xiaoyu
p.s. It should be mentioned that such a program runs smoothly when compiled with ifort, but gfortran gives an runtime error as shown above
--- Problem solved!
It seems that the issue lies in how ifortran and gfortran allocates memory. I defined in the code USV type which:
type USV
integer is_alloc
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
When initializing by
type(USV) Test_usv(:)
allocate(Test_usv(3)),
the value of is_alloc is 0 using intel fortran compiler, while arbitrary number for gfortran. I need to use this value as a criterion for allocating U V matrices:
if (is_alloc.eq.0) then
allocate(U%m(dim,dim))
end if
The fundamental problem is not a difference between ifort and gfortran. Your approach to the initialization of the variables isn't valid Fortran. Unless you initialize a variable with a declaration, assignment statement, etc., its value is undefined. One way to fix this would be to add a default initialization to the type definition:
type USV
integer is_alloc = 0
type (cmatrix) U,V
real(8), allocatable :: S(:)
end USV
Another approach would be to not track the allocation status yourself and to rely upon the intrinsic function that Fortran provides for this purpose:
if (.NOT. allocated (U%m) ) allocate(U%m(dim,dim))
P.S. Best practice is not to rely upon specific numeric values for kinds. The kind values are arbitrary and are not necessarily the number of bytes of the type. Some compilers use the number of bytes, others don't. One method to specify the number of bytes and to have portable code is to use types provided by the ISO Fortran environment:
use, intrinsic :: ISO_FORTRAN_ENV
integer(int32) :: d
real(real64), allocatable :: S(:)
The types are named after the number of bits. A list of the available types is in the "Intrinsic Modules" chapter of the gfortran manual.
I need a variable size array in Fortran. In C++ I would use vector. So I have a function like
integer function append(n, array, value)
integer, pointer, dimension(:) :: array
integer, pointer, dimension(:) :: tmp_arr
integer n
if (size(array) .eq. n) then
allocate(tmp_arr(2*size(array)))
tmp_arr(1:size(array)) = array
deallocate(array)
array => tmp_arr
end if
n = n + 1
array(n) = value
append = n
end function
that works fine if I use it the way
integer pos, val
pos = append(n, array, val)
However, if I would like to use it the way
integer i,j,n ! i,j<n
array(i) = append(n, array, array(j))
with gfortran this does not work. It compiles, but segfaults. The problem seems to be that gfortran makes addresses out of array(i) and array(j), sends the latter to the function append, and then when the address of array(j) is accessed and the one of array(i) written, the address space has been deallocated.
What I would like is that the value of array(j) is put on the stack (not the address) and then used in the function and after the function has finished the uptodate address of array(i) is looked up and the result of the function saved to it.
I am pretty sure gcc would do it the way I want, why is gfortran so mean?
Is there any way in Fortran to make a robust (meaning the array(j) = ... example works)
function or data type to have a c++ stl vector like behaviour?
Conclusion:
I eventually introduced temporary variables
integer tmp_val
tmp_val = value
...
array(n) = tmp_val
so at least the method can be called as
pos = append(n, array, array(j))
array(i) = pos
and hope that other/future developers on the project won't try to 'optimize' the two lines to eliminate the necessity of 'pos'.
Thanks for the answers and comments.
The answer by IRO-bot is the correct approach for Fortran 90. If you can limit yourself to compilers that support the Fortran 2003 MOVE_ALLOC intrinsic (included in gfortran since the 4.2 release), you can avoid one of the copies. That is, increasing the size of an array by a factor of 2 can be written as
allocate(tmp_arr(2*size(array)))
tmp_arr(1:size(array)) = array
deallocate(array)
move_alloc(tmp_arr, array)
! tmp_arr is now deallocated
OK, the problem is that you cannot deallocate and re-allocate the array that you are assigning a function value to. You are correct about the cause of your problem (arguments passed by reference and not by value as it is in C). Since you deallocate the array inside the function body, the assignment to that array becomes invalid, leading to segfault. This is not a gfortran issue, tried it with ifort and pgf90, all of them report the same problem. This works for me:
PROGRAM dynamic_size
INTEGER,DIMENSION(:),ALLOCATABLE :: array
ALLOCATE(array(10))
array=(/1,2,5,7,4,3,6,5,6,7/)
WRITE(*,*)SIZE(array)
CALL resize_array
WRITE(*,*)size(array)
CONTAINS
SUBROUTINE resize_array
INTEGER,DIMENSION(:),ALLOCATABLE :: tmp_arr
ALLOCATE(tmp_arr(2*SIZE(array)))
tmp_arr(1:SIZE(array))=array
DEALLOCATE(array)
ALLOCATE(array(size(tmp_arr)))
array=tmp_arr
ENDSUBROUTINE resize_array
ENDPROGRAM dynamic_size
Thank you a lot janneb.It was very helpful your comment.
Only a few change I have made was to omit the deallocate(array) . There was'nt any erro omitting this line in my code.
This change is specially helpful if you need to put it into a loop and you don't allocated array before the loop. My specific case follows below (look that I don't allocate x_all before or into the loop):
begin program test
integer,allocatable::x_all(:),tmp_arr(:)
integer,allocatable::x_tmp(:)
integer::N
allocate(x_tmp(2*N))
(...)
i=1
do while(logical test)
...
x_tmp(i)=some calculus
i=i+1
...
end do
i=i-1
allocate( tmp_arr( 1:(i+size(x_all) ) ) )
tmp_arr(1:size(x_all))=x_all
tmp_arr(size(x_all)+1:)=xtemp
call MOVE_ALLOC(tmp_arr,x_all)
...
end program