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
Related
Related questions Temporary array creation and routine GEMM
Warning message (402) : An array temporary created for argument
For the following Fortran code (modified from dsyev in fortran 90)
program test_dsyev
implicit none
integer, parameter :: dp = selected_real_kind(15, 307)
real(dp), allocatable :: A(:,:), A2(:,:,:), work (:), w(:)
real(dp) :: c1
integer :: i, j
integer :: lwork, info, n, lda
character :: jobz, UPLO
n=5
allocate(A(n,n))
allocate(A2(1,n,n))
A = 0.0_dp
c1 = 1.0_dp
do i = 1, n
do j = 1, n
A(i, j) = i * c1 + j * c1
end do
end do
A2(1,:,:) = A
lda = n
lwork = 10 * n
jobz = 'N'
UPLO = 'U'
allocate(work(lwork))
allocate(w(n))
call dsyev(jobz,uplo,n,A,lda,w,work,lwork,info)
write (*,*) w
call dsyev(jobz,uplo,n,A2(1,:,:),lda,w,work,lwork,info)
write (*,*) w
end program test_dsyev
called d.f90.
ifort -mkl -warn all -check all d.f90 gives me
-1.58312395177700 -1.465651932086313E-015 8.229374109843719E-018
4.803282588162841E-016 31.5831239517770
forrtl: warning (406): fort: (1): In call to DSYEV, an array temporary was created for argument #4
Image PC Routine Line Source
a.out 0000000000408E86 Unknown Unknown Unknown
a.out 000000000040580B Unknown Unknown Unknown
a.out 0000000000403812 Unknown Unknown Unknown
libc-2.17.so 00002B10A2A31555 __libc_start_main Unknown Unknown
a.out 0000000000403729 Unknown Unknown Unknown
-1.58312395177700 -1.465651932086313E-015 8.229374109843719E-018
it seems the warning message is related to A2(1,:,:) and memory non-continuous. Yes, Fortran is column-major. But, the first dimension of A2 is 1. A2 should follow A2(1,1,1), A2(1,2,1),..., I mean, the first index of A2 does not play a role in memory allocation. Or am I totally wrong?
Let's consider a much simpler program to look at what's going on:
implicit none
integer i(1,1)
call s(i(1,:))
contains
subroutine s(j)
integer j(*)
end subroutine s
end program
Compiled with -check to enable temporary copying checks we can see exactly the same warning:
forrtl: warning (406): fort: (1): In call to S, an array temporary was created for argument #1
Like the example of the question, the dummy argument has assumed size and the actual argument is an array section of the same rank, but one rank smaller than the whole array.
The array section i(1,:) actual argument is contiguous, so perhaps there's no need for a temporary copy for the dummy j? Looking at this, ifort makes a temporary copy when i is not simply contiguous, even if it's "obviously" contiguous.1
However, because the dummy argument j is assumed-size, we don't need to worry about passing the array section i(1,:): we can pass the whole array i, or the simply contiguous section i(:,:). We can do this because the ranks of the actual and dummy arguments do not need to match when the dummy is assumed-size (or explicit-size).
Note that if the dummy is assumed-shape, then the ranks do need to match, but the compiler may handle the argument association by passing an array descriptor/dope instead of making a temporary copy: unlike assumed- and explicit-size array arguments, it's not necessary for an assumed-shape dummy to be contiguous. (The compiler may still make, and warn about making, a temporary copy when it decides that is beneficial, but it isn't always going to happen.)
Finally, I am speculating that the trigger for a warning is simple contiguity (happy for someone in the know to confirm/dispute), but there's some evidence that you can avoid the runtime check warning with a simple alternative approach as here. (It makes complete sense for the copy to be made if not simply contiguous: simple contiguity is defined in a way to be easy to check.)
1 i(1,:) is not simply contiguous, even when the first extent is 1, because of the rule of simple contiguity (F2018, 9.5.4):
no subscript-triplet is preceded by a section-subscript that is a subscript.
The : in second place is a subscript triplet, yet 1 in first place is a subscript.
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 have a small code declaring a pointer to array of derived type which has a field that is an allocatable array of another derived type having a real variable as a field.
Using gnu fortran (8.2) I obtain different results for each location in the array or as a vector.
Using intel fortran (2019.4) compiler succeeded.
program test
implicit none
integer, parameter :: length = 2
real(8), dimension(length) :: a, b
integer :: i
type point
real(8) :: x
end type point
type stored
type(point), dimension(:), allocatable :: np
end type stored
type(stored), dimension(:), pointer :: std=>null()
allocate(std(1))
allocate(std(1)%np(length))
std(1)%np(1)%x = 0.3d0
std(1)%np(2)%x = 0.3555d0
do i = 1, length
write(*, "('std(1)%np(',i1,')%x = ',1e22.14)") i, std(1)%np(i)%x
end do
do i = 1, length
write(*, "('std(1)%np(1:',i1,') = ',2e22.14)") i, std(1)%np(1:i)%x
end do
a = std(1)%np(1:2)%x
b = [std(1)%np(1)%x, std(1)%np(2)%x]
if (norm2(a - b) .gt. 1d-3) then
write(*,*) 'failure'
else
write(*, *) 'success'
end if
end program test
the code terminates successfully, but using gfortran one obtains 'failure' on screen and inconsistent prints above and using Intel compiler one obtains 'success' on screen and consistent prints above.
It's not clear to me what your question is, unless it's the title. If so, no, pointers to derived types are fine. Your program correctly allocates std as an extent-1 array and then allocates std(1)%np(2). It then assigns to the x subcomponents of both np elements. Looks fine to me.
There are several things that could potentially cause "failure" - you should run the gfortran code in the debugger to see what is going wrong.
The problem presented in the code above was resolved in the following [link] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91077
This question already has an answer here:
Function in fortran, passing array in, receiving array out
(1 answer)
Closed 4 years ago.
INTEGER FUNCTION NUMTOLIST(NUM) RESULT(LI)
IMPLICIT NONE
! FUNCTION TO CONVERT NUMBERS INTO ARRAYS OF NUMBERS, BY DIGIT
! PROBLEM 1: X IS NOT BEING PASSED
INTEGER :: NUM
INTEGER :: I,J
INTEGER :: LI(0:5) ! G95 COMPILER SAYS THAT 'LI ALREADY HAS BASIC TYPE INTEGER', DOESN'T RECOGNISE AS ARRAY
PRINT *,'NUM: ',NUM ! DEBUGGING LINE, CONFIRMS THAT X IS NOT BEING PASSED INTO THE FUNCTION
DO I = 0,5
J = 0
DO WHILE (J*10**(5-I)<=NUM)
J = J+1
END DO
J = J-1
LI(I) = J
NUM = NUM-(J*10**(5-I))
END DO
PRINT *,'LI: ',LI ! DEBUGGING LINE, SHOWS THAT FUNCTION IS AS IS SUPPOSED TO, EXCEPT FOR ON SOME SORT OF DEFAULT INTEGER
RETURN
END FUNCTION NUMTOLIST
PROGRAM NUMTOLISTTEST
IMPLICIT NONE
INTEGER FUNCTION, NUMTOLIST
INTEGER :: X
INTEGER :: F(1:6)
READ *,X
PRINT *,X
F = NUMTOLIST(X)
! PROBLEM 2: THE RESULT OF NUMTOLIST SHOULD BE A LIST, BUT F IS JUST BEING ASSIGNED NUM, NOT THE OUTPUT OF NUMTOLIST
PRINT *,F
READ *,X
END PROGRAM NUMTOLISTTEST
Here is my full code. As the name suggests, this is a test for a function of a larger code. There are several problems but the one that is most pressing is that for some reason a variable is not being passed to a function. I am using the silverfrost compiler, and for some reason although everything in the function itself is working as it should, it neither inputs nor returns properly. The input itself is completely disregarded, leading to num being undefined, and the return isn't being read as a list type, as F, when printed, is a list of the arbitrary number num took on. It's completely beyond me why any of this is happening, and I've been on and off looking at this for a couple of days.
Much more trivially, when I try to compile using G95, it won't. It claims that 'Li already has basic type 'Integer'', and then won't recognise Li as a list. The method I'm using to declare an integer as a list has worked in the past for me, and fits the documentation I've seen, so I'm confused why it's throwing an error.
I've been stuck on this for a while, and I just can't seem to fix it on my own. Whatever help is offered will be greatly appreciated, and thank you in advance.
You are indeed declaring a type for the variable LI twice in the function. Look:
INTEGER FUNCTION NUMTOLIST(NUM) RESULT(LI)
! (...)
INTEGER :: LI(0:5)
Notice the INTEGER keyword before function declaration. It applies to the variable name declared as result in result(LI). The second declaration of LI gives the error.
Solutions:
Remove the INTEGER from the function declaration (preferred);
Remove the second type declaration of LI. You can specify a dimension without type declaration, with the dimension specification statement.
like this:
DIMENSION LI(0:5)
Besides that, to call a function with an array as a returning value, you will need an explicit interface.
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.