How to understand this negative index array variable declaration? [duplicate] - fortran

I need in a program to pass some allocatable arrays to subroutines, and i need to know if the way I do it are in the standard or not.
If you know where I can search for the standard of fortran, Tell me please.
Here is a little code that will better explain than words
program test
use modt99
implicit none
real(pr), dimension(:), allocatable :: vx
allocate(vx(-1:6))
vx=(/666,214,558,332,-521,-999,120,55/)
call test3(vx,vx,vx)
deallocate(vx)
end program test
with the module modt99
module modt99
contains
subroutine test3(v1,v2,v3)
real(pr), dimension(:), intent(in) :: v1
real(pr), dimension(0:), intent(in) :: v2
real(pr), dimension(:), allocatable, intent(in) :: v3
print*,'================================'
print*,v1(1:3)
print*,'================================'
print*,v2(1:3)
print*,'================================'
print*,v3(1:3)
print*,'================================'
end subroutine test3
end module modt99
on screen, I get
================================
666.000000000000 214.000000000000 558.000000000000
================================
214.000000000000 558.000000000000 332.000000000000
================================
558.000000000000 332.000000000000 -521.000000000000
================================
So are the three ways of dummy arguments in subroutine test3 legal (in what version of fortran, 90, 95, 2003?) and are their behavior normal?

The first version passes the array slice to the subroutine. Note that boundary information are not passed along in this way, arrays are assumed to start at 1 and go to size(array).
The second way is just like the first one, but you manually set the lower boundary to 0, that's why printing v3(1:3) gives you the values with an offset of 1.
The third way passes all array information to the subroutine (including boundaries), hence the "correct" indexing. Passing allocatable arrays was introduced with Fortran 2003.
Apart from the fact that you have an aliasing issue (passing the same variable to three different dummy arguments), all three versions are legal.
You can find all documents of the standards here.
Especially, take a look at the Fortran 2003 Standard, Ch. 5.1.2.5 DIMENSION attribute to see the differences between assumed shape and deferred shape arrays in dummy arguments.

Related

How can I resolve the problem about intrinsic assignment between two polymorphic components into a type variables using Gfortran?

Gfortran 8.1 and 9.1 give me an error about intrinsic assignment between two polymorphic components into a type variables. I don't have any problem using intel compiler but no in the case in gfortran. I'm asking if someone know any workaround.
Here an example that you can try to compile.
Program Check
implicit none
!> Type definitions
Type :: Atm_Type
End Type Atm_Type
Type, extends (Atm_type) :: Atm_Std_Type
End Type Atm_Std_Type
Type, extends (Atm_std_type) :: Atm_Ref_Type
End Type Atm_Ref_Type
Type :: AtList_Type
integer :: Natoms
class(Atm_Type), dimension(:), allocatable :: Atom
end Type AtList_Type
!> Variables
type(AtList_Type) :: list
call sub(list)
Contains
Subroutine Sub(List)
!---- Argument ----!
type (AtList_Type), intent(in out) :: List
!---- Local Variables ----!
integer :: i
type (AtList_Type), allocatable :: local
if (List%natoms <= 0 ) return
allocate(local%atom(List%natoms))
do i=1, List%natoms
local%atom(i)=list%atom(i)
end do
End Subroutine Sub
End Program Check
Here the workaround is very simple and appeared in one of the recent questions/answers. Just copy the whole array
local%atom = list%atom
However, it is not always possible to do that when you really need to access individual elements. If your real use case is like that, show the real use case.
If the number of possible types inside is limited, you can also use the select type type guard, but often that is not possible either.

Writing assumed-size array causes "upper bound shall not be omitted..."

I am writing code to add on a closed-source Finite-Element Framework that forces me (due to relying on some old F77 style approaches) in one place to rely on assumed-size arrays.
Is it possible to write an assumed-size array to the standard output, whatever its size may be?
This is not working:
module fun
implicit none
contains
subroutine writer(a)
integer, dimension(*), intent(in) :: a
write(*,*) a
end subroutine writer
end module fun
program test
use fun
implicit none
integer, dimension(2) :: a
a(1) = 1
a(2) = 2
call writer(a)
end program test
With the Intel Fortran compiler throwing
error #6364: The upper bound shall not be omitted in the last dimension of a reference to an assumed size array.
The compiler does not know how large an assumed-size array is. It has only the address of the first element. You are responsible to tell how large it is.
write(*,*) a(1:n)
Equivalently you can use an explicit-size array
integer, intent(in) :: a(n)
and then you can do
write(*,*) a
An assumed-size array may not occur as a whole array reference when that reference requires the shape of the array. As an output item in a write statement that is one such disallowed case.
So, in that sense the answer is: no, it is not possible to have the write statement as you have it.
From an assumed-size array, array sections and array elements may appear:
write (*,*) a(1:2)
write (*,*) a(1), a(2)
write (*,*) (a(i), i=1,2)
leading simply to how to get the value 2 into the subroutine; at other times it may be 7 required. Let's call it n.
Naturally, changing the subroutine is tempting:
subroutine writer (a,n)
integer n
integer a(n) ! or still a(*)
end subroutine
or even
subroutine writer (a)
integer a(:)
end subroutine
One often hasn't a choice, alas, in particular when associating a procedure with a dummy procedure with a specific interface . However, n can get into the subroutine through any of several other ways: as a module or host entity, or through a common block (avoid this one if possible). These methods do not require modifying the interface of the procedure. For example:
subroutine writer(a)
use aux_params, only : n
integer, dimension(*), intent(in) :: a
write(*,*) a(1:n)
end subroutine writer
or we could have n as an entity in the module fun and have it accesible in writer through host association. In either case, setting this n's value in the main program before writer is executed will be necessary.

Function declared in module using interface

I have a code as follows:
The function is declared in a module using an interface block
module my_subs
implicit none
interface
function cross(a,b)
integer, dimension(3) :: cross
integer, dimension(3), intent(in) :: a, b
end function cross
end interface
end module my_subs
program crosstest
use my_subs
implicit none
integer, dimension(3) :: m, n
integer, dimension(3) :: r
m = [1, 2, 3]
n = [4, 5, 6]
r = cross(m,n)
write(*,*) r
end program crosstest
function cross(a,b)
implicit none
integer, dimension(3) :: cross
integer, dimension(3), intent(in) :: a, b
cross(1) = a(2)*b(3) - a(3)*b(2)
cross(2) = a(3)*b(1) - a(1)*b(3)
cross(3) = a(1)*b(2) - a(2)*b(1)
end function cross
According to this website, the use of interface blocks allows main programs and external subprograms to interface appropriately. However, I tested different mismatch of array size scenarios, I got the following result:
Change dimension at line 6 to 2 and 4, the code cannot be compiled;
Change dimension at line 7 to 2, the code can be compiled and produce the correct output;
Change dimension at line 7 to 4, the code cannot be compiled;
Change dimension at line 27 to 2 and 4, the code can be compiled and produce the correct output;
Change dimension at line 28 to 2 and 4, the code can be compiled and produce the correct output;
I am confuse about the different scenarios I performed, because I suppose the use of interface can help me to detect any mismatch of array size. In this case, is it better for me to move the function cross(a,b) into the module my_subs using contains?
You can check the interface by putting some declarations in function cross that test whether the interface as declared in module my_subs matches what function cross thinks its interface should be:
interface in function cross:
module my_subs
implicit none
interface
function cross(a,b)
integer, dimension(3) :: cross
integer, dimension(3), intent(in) :: a, b
end function cross
end interface
end module my_subs
program crosstest
use my_subs
implicit none
integer, dimension(3) :: m, n
integer, dimension(3) :: r
m = [1, 2, 3]
n = [4, 5, 6]
r = cross(m,n)
write(*,*) r
end program crosstest
function cross(a,b) result(res)
use my_subs, only: check => cross
implicit none
integer, dimension(3) :: res
integer, dimension(3), intent(in) :: a, b
procedure(check), pointer :: test => cross
res(1) = a(2)*b(3) - a(3)*b(2)
res(2) = a(3)*b(1) - a(1)*b(3)
res(3) = a(1)*b(2) - a(2)*b(1)
end function cross
gfortran zaps this in all cases of mismatch you tested. I'm not sure that it should: if TKR of a dummy argument matches, shouldn't the rules of sequence association produce a correct invocation of the procedure? I haven't used submodules, but I think that they might do roughly the same thing as my example does.
When using an interface block to provide an explicit interface within a scope (in this case, in the module, which is then used by the main program) it is a requirement on the programmer that the specified interface matches the actual procedure.1
As given first, these things match happily. Changing the size of the function result or dummy arguments of one statement of the procedure but not the other creates a mismatch. Such code is a violation of the Fortran standard.
In general, a compiler isn't required to detect this violation. It may take your interface block on faith or it may do some extra work to see whether it should believe you. This latter is possible, especially if the external procedure is given in the same file as the interface block. You may see some error messages relating to this.
Further, if the interface block is given, the compiler will test the reference to the procedure against the interface, not the procedure's actual definition.
One failing, on the programmer's part, is if the actual argument isn't compatible with the dummy argument. Such a case is when the dummy argument is an explicit shape array but the actual argument is smaller than the dummy argument. [It isn't an error to have the actual argument larger.]
This problem is one of your cases:
interface
function cross(a,b)
integer, dimension(3) :: cross
integer, dimension(4), intent(in) :: a, b ! Dummy extent 4
end function cross
end interface
print*, cross([1,2,3], [4,5,6]) ! Actuals extent 3
end
Again, a compiler isn't obliged to notice this for you. It's being nice in the case where it can detect the problem.
Now, in another case you are stating that the function result is an array of shape [4] or [2]. But you are trying to assign that to an array of shape [3]. That won't work.
In conclusion, if you use an interface block to provide an explicit interface for an external procedure make sure it is correct. Turning the external procedure into a module procedure means it's the compiler's responsibility, not yours, to have the correct interface visible.
1 "Matching" here means that the characteristics of the procedure as stated by the interface block are consistent with the procedure's definition. The extents of the function result and dummy arguments are part of this. A procedure defined pure needn't have the interface block stating it is pure.

Array of unknown rank as subroutine argument

I am designing a module which works with the hdf5 Fortran library. This module contains subroutines to read and write arrays of different types and shapes to/from a file.
e.g. I wish to be able to call writeToHDF5(filepath, array) regardless of what the shape and type of array is. I realise that interfaces have to be used to achieve this with different types. I am however wondering if it is possible to have an assumed shape of the array.
e.g.
if an array was defined such as
integer(kind=4), dimension(*),intent(in) :: array
and a two dimensional array was passed this would work. Is there any way to do this without creating separate subroutines for each shape of the array?
As Vladimir F says, Fortran 2015 adds "assumed-rank" - this is useful Fortran-Fortran (it was requested by MPI for the Fortran bindings), but when you receive such an array, you can't do much with it directly without additional complications. Several compilers support this already, but few (if any?) support the newly added SELECT RANK construct that make this a bit more useful.
You can, however, use C_LOC and C_F_POINTER to "cast" the assumed-rank dummy to a pointer to an array of whatever rank you like, so that's a possibility.
The standard (even back to Fortran 90) does give you an out here. If you write:call writeToHDF5(filepath, array(1,1)) (assuming array is rank 2 here), the explicit interface of the called procedure can specify any rank for the dummy argument through the magic of "sequence association". There are some restrictions, though - in particular the array is not allowed to be assumed-shape or POINTER.
I know that this comes out late, but for any future readers - the answer is actually yes.
This is a simple example of a procedure to read a single data-set of integers with any given shape. The inputs needed were read from the HDF5 using a single routine which does not require any special specification and are the same for integer, real, string and so on.
I have tested it on a "0-D" array (so size of (/1/) ), 1-D, 2-D, 3-D and 4-D arrays.
In all cases, the data was retrieved properly.
(Note: I removed some checks regarding the Errorflag, as they are not critical for the example)
subroutine ReadSingleDataset_int(FileName,DataName,DataType,Data_dims,Errorflag,InputArray)
implicit none
character(len=*), intent(in) :: FileName,DataName
integer(HID_T), intent(in) :: DataType
integer(hsize_t), dimension(:), intent(in) :: Data_dims
logical, intent(out) :: ErrorFlag
integer, dimension(*), intent(inout) :: InputArray
integer :: hdferr
integer(HID_T) :: file_id,dset_id
ErrorFlag=.FALSE.
IF (.NOT.HDF5_initialized) THEN
CALL h5open_f(hdferr)
HDF5_initialized=.TRUE.
ENDIF
call h5fopen_f(trim(FileName),H5F_ACC_RDONLY_F, file_id, hdferr)
call h5dopen_f(file_id,trim(DataName),dset_id,hdferr)
call h5dread_f(dset_id,DataType,InputArray,Data_dims,hdferr)
call h5dclose_f(dset_id,hdferr)
call h5fclose_f(file_id,hdferr)
end subroutine ReadSingleDataset_int
My main interest in this now is - would it be possible to replace the type/class specific (integer/real/etc') with a generic one on the lines of:
class(*), dimension(*), intent(inout) :: InputArray
As I have several routines like that (for int, real, string) which only vary in that specification of the input type/class. If that limitation could be alleviated as well, that will be even more elegant.
(Just to point out the issue - the h5dread_f does not accept the buffer argument, InputArray in my case, to be unlimited polymorphic)

Fortran: Variables changing on their own

I'm having a problem of variables getting over-written for I don't know what reason. I've posted a chunk of the code below so you can see how things are declared. The variables strain,Qi,Qf,Qd,tel and Gc are passed into the subroutine and used to calculate ssgrad,strn0,strss0.
My problem is that tel and Gc are passed into the subroutine OK but are for some reason change value during this chunk of code.
Using print statements I've found that the problem first occurs during the 2nd do loop. When I set strss0 to 0, Gc and tel change value from both being equal to 1, to seemingly random numbers: tel=11.52822 Gc=-8.789086 (Just shown for the sake of example)
Each time I run the code they are set to the same values though.
Just to let you know, this subroutine interfaces with a commercial finite element package.
Many thanks in advance for any help with this
subroutine initcalcs(strain,Qi,Qf,Qd,tel,Gc,ssgrad,strn0,strss0)
implicit none
integer :: i,j
real*8:: nstrn0,nstrs0,strn0,strnf,varsq,normvar,lmbda0,lmbdaf,
# ssgrad,t0,tt,tel,nstrnf,nstrsf,Gc
real*8, dimension(3) :: strain,stran0,stranf,strss0,strssf,var
real*8, dimension(3,3) :: Qd,Qi,Qf
lmbda0=1.0d0
nstrn0=0.0d0
do i=1,3
stran0(i)=0.0d0
stran0(i)=strain(i)*lmbda0
nstrn0=nstrn0+stran0(i)**2
end do
nstrn0=dsqrt(nstrn0)
do i=1,3
strss0(i)=0.0d0
end do
In Fortran, there are two common causes of the corruption of memory values. One is a subscript error, where you assign to an array element using an incorrect subscript value. This writes to a memory location outside of the array. The other is a disagreement between the arguments in the call to a procedure (subroutine or function) and the dummy arguments of the procedure. Either can cause problems to appear at source code locations different from the actual cause. Suggestions: inspect your code for these problems. Turn on stringent warning and error checking options of your compiler. The use of Fortran >=90 and modules gives Fortran much better ability to automatically find argument consistency problems. You could monitor the memory locations with a debugger and see what it modifying it.
I concur with M. S. B.: turn on stringent warnings and error checking and verify the subroutine calls are passing arguments that have the same type and shape (array dimensions) as the subroutine expects.
The colons in the variable declaration syntax imply this is Fortran90 or later. If that's the case, I strongly suggest using the INTENT modifier to specify whether arguments are intended to be read-only.
For example, let's assume that of the arguments passed to this routine, strain, Qi, Qf, Qd, tel, and Gc are read-only input and the arguments are ssgrad, strn0, and strss0 are returned as output; that is, whatever value they have is overwritten by this routine.
The variable declarations for the arguments would change to:
real*8, dimension(3), intent(in) :: strain
real*8, dimension(3,3), intent(in) :: Qi, Qf, Qd
real*8, intent(in) :: tel, Gc
real*8, intent(out) :: strn0, ssgrad
real*8, dimension(3), intent(out) :: strss0
The INTENT keyword is an addition to Fortran 90 which allows the user to specify which arguments are read-only (INTENT(IN)), initialized but which may be modified within the routine (INTENT(INOUT)) and which are treated as uninitialized and will be set within the routine (INTENT(OUT)).
If INTENT is not specified, it is defaults to INOUT which is consistent with FORTRAN 77 (Note that there are minor differences between INTENT(INOUT) and INTENT not being specified but they aren't relevant in this example).
A good compiler will throw an error if a routine tries to assign a value to a variable declared INTENT(IN) and will at least throw a warning if a variable declared INTENT(OUT) doesn't get assigned a value.
If possible, set INTENT(IN) on all the variables which are supposed to be read-only. This may not be possible, depending on how those variables are passed to other routines. If INTENT isn't specified on arguments to routines called within this routine, it will default to INOUT. If you pass an INTENT(IN) variable as an INTENT(INOUT) argument, the compiler will throw an error. If this is happening in code you control, you have have to specify INTENT in a number of routines. This may or may not be desirable depending on whether you want to generally improve your code or just fix this one problem really quickly.
I'm assuming some of these variables are passed to external routines in the finite element package which I 'm guessing is linked to your code rather than compiled; I'm not sure how compile-time intent checking is handled in that case.