I have actually a nested question :
Does the order of variable declaration matter in Fortran?
If yes, what is the best order to declare variables?
For example, is this program :
PROGRAM order1
IMPLICIT NONE
DOUBLE PRECISION,DIMENSION(:,:),ALLOCATABLE:: array_double_2D
DOUBLE PRECISION,DIMENSION(:),ALLOCATABLE:: array_double_1D
INTEGER,DIMENSION(:),ALLOCATABLE:: array_int_1D
INTEGER :: int1,int2
LOGICAL :: boolean1,boolean2
... instructions ...
better than this one :
PROGRAM order2
IMPLICIT NONE
LOGICAL :: boolean1,boolean2
INTEGER :: int1,int2
INTEGER,DIMENSION(:),ALLOCATABLE:: array_int_1D
DOUBLE PRECISION,DIMENSION(:),ALLOCATABLE:: array_double_1D
DOUBLE PRECISION,DIMENSION(:,:),ALLOCATABLE:: array_double_2D
... instructions ...
?
(by "better", I mean efficient in memory management and faster)
Thanks for your answers!
No, the order does not matter, unless your declaration depends on a previously declared entity.
Obviously
integer, parameter :: arr(*) = [1,2,3]
integer :: arr2(size(arr))
must use this order, because you refer to another entity.
If they don't depend on each other it does not matter. It does not matter for efficiency in any way. For style everybody can have his own opinion what is the nicest order, no reason to discuss that here.
It could matter in a common block, because then you can force an array to start at an inconvenient address in memory and be more difficult to vectorize.
It does also matter in certain type declarations:
type t1
sequence
integer(int32) :: field1
integer(int16) :: field2
end type
will be laid out in memory differently than
type t2
sequence
integer(int16) :: field2
integer(int32) :: field1
end type
and that one differently than
type t3
integer(int16) :: field2
integer(int32) :: field1
end type
because without sequence the compiler is free to insert some padding and it will typically do so in t3.
Interoperable types
type, bind(C) :: t3
...
also enforce order of the components, but the compiler can include the padding for performance. It will use the C compiler's rules for padding.
I would do this....
PROGRAM order1
IMPLICIT NONE
!DIR$ ATTRIBUTES ALIGN: array_double_2D::64
DOUBLE PRECISION,DIMENSION(:,:),ALLOCATABLE:: array_double_2D
!DIR$ ATTRIBUTES ALIGN: array_double_1D::64
DOUBLE PRECISION,DIMENSION(:) ,ALLOCATABLE:: array_double_1D
!DIR$ ATTRIBUTES ALIGN: array_int_1D::64
INTEGER,DIMENSION(:) ,ALLOCATABLE:: array_int_1D
INTEGER :: int1,int2
LOGICAL :: boolean1,boolean2
... instructions ...
Then there is no doubt that the arrays are on 64 byte boundaries.
There are also compiler switch options. In fort it is '-align array64byte'.
This will only make a difference if/when you vectorise, which you should want to be doing... Hence you should align the arrays/vectors somehow.
Related
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.
Is it right to have optional component in the derived type. For example, the variable 'fname_new' in the code snippet below. If not, what is the way around? I want to include 'fname_new' optionally depending upon whether the source is of type 1 or 2.
TYPE, PUBLIC :: species
CHARACTER(LEN=12) :: spname
CHARACTER(LEN=12) :: source
CHARACTER(LEN=20) :: fname
CHARACTER(LEN=12) :: field
CHARACTER(LEN=20),OPTIONAL :: fname_new
END TYPE species
The number of components must be known at compile time so you cannot have an optional component. However, you can have an allocatable component. In your case:
type :: species
...
character(len=:), allocatable :: fname_new
end type
A different approach would be to construct some sort of class hierarchy. This would have the benefit of encoding information in types rather than strings.
Edit: As #VladimirF pointed out, this approach requires fortran-2003.
Realistically, If the fname_new component is really only be 20 chars long, then you won't save a whole lot of space by making it an allocatable. On an x86/64 bit architecture, the allocatable will be an 8 byte pointer and will force some sort of alignment on your type that will eat a few more bytes. I might just leave it as a character(len=20).
I'm trying to pass an argument declared simply as logical :: invar
to a function where the receiving variable is declared as
logical(x) :: invar
Now x is defined as
INTEGER, PARAMETER :: x = KIND(.TRUE.)
What does that definition of x mean? I did a search for kind(.true.) but all results kind of brush this aspect aside. Would appreciate some clarification for an expert.
I'm using the Intel compiler, if this has something to do with the compiler.
Variables such as real numbers, integers, and even logicals can be different kinds. Typically, this is important to distinguish between single precision and double precision reals, for example. In my experience, there's no reason to fiddle with the kind of a logical.
Whoever wrote this code, obviously, thinks otherwise. When you declare a logical in the usual way, with
logical :: L1
the variable my_logical is of the default kind. When you declare it using
integer, parameter :: x = KIND(.TRUE.)
integer(x) :: L2
it has the kind of x, which is defined to be the kind of .true.. The tricky part is that .true. is almost certainly also of the default kind. (The standard requires that the default kind is the kind of .FALSE.)
So, in the examples above, L1 and L2 are of the same kind. I don't know why somebody would bother with defining the default logical kind as x, but you shouldn't worry about it.
I would like to interpret a Fortran (real*8, say) array as an array of bytes, so that it can be sent to a function to process things on the byte level. What's a simple (preferably no-copy) way to accomplish this?
First, it is not clear what is a function working on byte level. Does it use Fortran characters? Or 1-byte integers? They are different beasts in Fortran.
You could try to lie about the signature of your function and just pass the array as it is. Likely to work, not strictly standard conforming.
Transfer() is the best modern tool for similar purposes, but it may indeed involve temporaries.
If the size of the array is fixed (it is not allocatable or pointer or dummy argument) you could use equivalence which is quite similar to union in C.
But you must be careful about what is allowed, this is a notoriously dodgy area. Even the C union rules differ from the C++ rules. Fortran equivalence has its own rules and more strict, I am afraid. Type punning is not allowed, but a lot of code in the wild does it.
Doing tricks with C pointers and pointing to the same array from different pointers with different types is definitely not standard conforming and may give you expected results in some cases and wrong results in others (undefined behaviour as they call it in C and C++).
A "NO_COPY" way...but relies on DEC extensions:
USE ISO_C_BINDING
IMPLICIT NONE
UNION
MAP
REAL(KIND=C_DOUBLE) , DIMENSION(N) :: R8_Data
END MAP
MAP
BTYE , DIMENSION(N*8) :: B_Data
END MAP
MAP
CHARACTER(LEN=1) , DIMENSION(N*8) :: C_Data
END MAP
MAP
INTEGER(KIND=C_Int16_T), DIMENSION(N*4) :: I2_Data
END MAP
MAP
INTEGER(KIND=C_Int32_T), DIMENSION(N*2) :: I4_Data
END MAP
END UNION
#Valdimir equivalence also works if one does not have access to the DEC extensions.
There is a slated upgrade to gfortran to add in the MAP and UNION DEC extensions, so in time it will be there too.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56226
My appreciation of the difference is back on track...
One can use UNION/MAP inside of a structure. Outside a structure/TYPE then EQUIVALENCE does all one needs.
So As Vladimir mentioned this also is a "no copy"...
REAL(KIND=C_DOUBLE) , DIMENSION(N) :: R8_Data
BTYE , DIMENSION(N*8) :: B_Data
CHARACTER(LEN=1) , DIMENSION(N*8) :: C_Data
INTEGER(KIND=C_Int16_T), DIMENSION(N*4) :: I2_Data
INTEGER(KIND=C_Int32_T), DIMENSION(N*2) :: I4_Data
EQUIVALENCE(R8_Data, I4_Data)
It is almost more dangerous than it is worth, unless one has a specific problem.
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)