Simple custom linked list in Fortran - Unexpected behavior - list

I'm trying to create a very simple linked list "class" in Fortran. I've based my Fortran code in the following C example:
https://www.tutorialspoint.com/learn_c_by_examples/simple_linked_list_program_in_c.htm
Sometimes I compile and run the code and I get the expected output, a "list" containing nodes linking to other nodes, with the ordered values:
5, 4, 3, 2, 1
However, some other times, with the same exact code, I get something like:
5, 4, 3, 2, 4, 3, 2, 4, 3, 2, 4, 3, 2, ... ! or
5, 4, 3, 3, 3, 3, 3, 3, ...
My guess is some memory allocation issue? I'm new to Fortran and new to using pointers and I simply don't know where to start to debug these kinds of problems...
class_list.f90
MODULE CLASS_LIST
PRIVATE
TYPE :: NODE
REAL :: VALUE
TYPE(NODE), POINTER :: NEXT
END TYPE
TYPE, PUBLIC :: LIST
TYPE(NODE), POINTER :: HEAD
CONTAINS
PROCEDURE :: APPEND
END TYPE
CONTAINS
SUBROUTINE APPEND(THIS, VALUE)
CLASS(LIST), INTENT(INOUT) :: THIS
REAL, INTENT(IN) :: VALUE
TYPE(NODE), ALLOCATABLE, TARGET :: LINK
ALLOCATE(LINK)
LINK.VALUE = VALUE
LINK.NEXT => THIS.HEAD
THIS.HEAD => LINK
END SUBROUTINE
END MODULE
main.f90
PROGRAM MAIN
USE CLASS_LIST
TYPE(LIST) :: A
INTEGER :: I
DO I = 1, 5, 1
CALL A.APPEND(REAL(I))
END DO
END PROGRAM
Expected behavior (main local variables in Visual Studio):
Unexpected behavior example (main local variables in Visual Studio):

You have three problems, as suggested in the comments
In Fortran the structure component selector is %, NOT . . Thus your program contains syntax errors
In Fortran when an allocatable array goes out of scope it is automatically deallocated, unless it has the save attribute. This is a good thing as it means memory leaks using allocatable arrays are not possible, but it hurts you here as when you exit the append routine the array LINK is deallocated, so you lose your data. This is not what you want, and you end up with a dangling pointer - and so any behaviour is possible, including even appearing to work. You can avoid this here by using a pointer instead of an allocatable array. This works here as pointers are not deallocated automatically on going out of scope, but it does mean memory leaks and other strange behaviour is much more likely, so generally you should try to use allocatable arrays rather than pointers wherever possible.
In Fortran the initial association status of a pointer is undefined unless you initialise it. As undefined pointers can lead to strange behaviour it is best to initialise explicitly using => null()
In fact once you have fixed item 1 gfortran at least can tell you there is a problem if you turn on all the warning flags. The below contains this fix, and also a printing routine which you omit. Look at the warning produced by the compiler:
ijb#ianbushdesktop ~/work/stack $ cat link_alloc.f90
Module CLASS_LIST
Private
Type :: NODE
Real :: Value
Type(NODE), Pointer :: NEXT
End Type NODE
Type, Public :: LIST
Type(NODE), Pointer :: HEAD
Contains
Procedure :: APPEND
Procedure :: Print
End Type LIST
Contains
Subroutine APPEND(THIS, Value)
Class(LIST), Intent(INOUT) :: THIS
Real, Intent(IN) :: Value
Type(NODE), Allocatable, Target :: LINK
Allocate(LINK)
LINK%Value = Value
LINK%NEXT => THIS%HEAD
THIS%HEAD => LINK
End Subroutine APPEND
Subroutine Print( this )
Class( list ), Intent( In ) :: this
Call descend( this%head )
Contains
Recursive Subroutine descend( head )
Type( node ), Intent( In ) :: head
Write( *, '( f5.0, 1x )' ) head%value
If( Associated( head%next ) ) Then
Call descend( head%next )
End If
End Subroutine descend
End Subroutine Print
End Module CLASS_LIST
Program MAIN
Use CLASS_LIST
Type(LIST) :: A
Integer :: I
Do I = 1, 5, 1
Call A%APPEND(Real(I))
End Do
Call a%print
End Program MAIN
ijb#ianbushdesktop ~/work/stack $ gfortran -std=f2008 -Wall -Wextra -fcheck=all -O -g link_alloc.f90 -o link_alloc
link_alloc.f90:26:4:
THIS%HEAD => LINK
1
Warning: Pointer at (1) in pointer assignment might outlive the pointer target [-Wtarget-lifetime]
ijb#ianbushdesktop ~/work/stack $
The English is a little cryptic, but what it is really telling you about is point 2 above - as the array is about to be deallocated the pointer outlives what it points at. Compiler warnings are really useful, learn how to use them! Similarly the run time checks (-fcheck=all) indicate how broken everything is:
ijb#ianbushdesktop ~/work/stack $ ./link_alloc
0.
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x7f470401f4af in ???
#1 0x7f4704ca3c49 in get_float_string
at ../../../gcc-7.4.0/libgfortran/io/write_float.def:1065
#2 0x7f4704ca4fe7 in write_float_0
at ../../../gcc-7.4.0/libgfortran/io/write.c:1597
#3 0x7f4704c9c9b4 in formatted_transfer_scalar_write
at ../../../gcc-7.4.0/libgfortran/io/transfer.c:2041
#4 0x7f4704c9cf4c in formatted_transfer
at ../../../gcc-7.4.0/libgfortran/io/transfer.c:2279
#5 0x40098a in descend
at /home/ijb/work/stack/link_alloc.f90:41
#6 0x4009a9 in descend
at /home/ijb/work/stack/link_alloc.f90:43
#7 0x4009d2 in __class_list_MOD_print
at /home/ijb/work/stack/link_alloc.f90:33
#8 0x400b1c in MAIN__
at /home/ijb/work/stack/link_alloc.f90:64
#9 0x400b1c in main
at /home/ijb/work/stack/link_alloc.f90:55
Segmentation fault
ijb#ianbushdesktop ~/work/stack $
Fixing points 2 and 3 above by using a pointer for the new node, and explicitly initialising the pointers, leads to
ijb#ianbushdesktop ~/work/stack $ cat link_pointer.f90
Module CLASS_LIST
Private
Type :: NODE
Real :: Value
Type(NODE), Pointer :: NEXT => Null()
End Type NODE
Type, Public :: LIST
Type(NODE), Pointer :: HEAD => Null()
Contains
Procedure :: APPEND
Procedure :: Print
End Type LIST
Contains
Subroutine APPEND(THIS, Value)
Class(LIST), Intent(INOUT) :: THIS
Real, Intent(IN) :: Value
Type(NODE), Pointer :: LINK
Allocate(LINK)
LINK%Value = Value
LINK%NEXT => THIS%HEAD
THIS%HEAD => LINK
End Subroutine APPEND
Subroutine Print( this )
Class( list ), Intent( In ) :: this
Call descend( this%head )
Contains
Recursive Subroutine descend( head )
Type( node ), Intent( In ) :: head
Write( *, '( f5.0, 1x )' ) head%value
If( Associated( head%next ) ) Then
Call descend( head%next )
End If
End Subroutine descend
End Subroutine Print
End Module CLASS_LIST
Program MAIN
Use CLASS_LIST
Type(LIST) :: A
Integer :: I
Do I = 1, 5, 1
Call A%APPEND(Real(I))
End Do
Call a%print
End Program MAIN
This compiles without warning and runs correctly repeatedly:
ijb#ianbushdesktop ~/work/stack $ gfortran -std=f2008 -Wall -Wextra -fcheck=all -O -g link_pointer.f90 -o link_pointer
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.
ijb#ianbushdesktop ~/work/stack $ ./link_pointer
5.
4.
3.
2.
1.

Related

Fortran, passing array using starting index only

In the program below, there are two methods presented for passing an array:
program main
integer, dimension(4) :: x = [9, 8, 7, 6]
call print_x(x(2:3)) ! Method 1
call print_x(x(2)) ! Method 2
end program
subroutine print_x(x)
integer, dimension(2), intent(in) :: x
print *, x
end subroutine
Both methods produce the same result: the numbers 8 and 7 are printed. Personally, I would never code this using Method 2 because it looks like a single value is being passed rather than an array.
Can you give an example of when Method 2 MUST be used instead of Method 1?
Consider the program
implicit none
integer :: x(2,2)=0
call set(x(2,1))
print*, x
contains
subroutine set(y)
integer y(2)
y = [1,2]
end subroutine set
end program
The dummy argument y in this subroutine call is argument associated with the elements x(2,1) and x(1,2). There is no array section of x which consists of exactly these two elements.

Data transfer element cannot have ALLOCATABLE components in gfortran

I am compiling a code in Fortran. If I use a recent version of Intel Fortran it compiles. However, I need to compile the same code with gfortran (I have gcc version 6.3.0 (GCC)).
At these two lines of the code
read(F%Unit) FileSettings%TCosmoTheoryParams
Write(F%Unit) CosmoSettings%TCosmoTheoryParams
I get the error `
Error: Data transfer element at (1) cannot have ALLOCATABLE components unless it is processed by a defined input/output procedure`
How to modify the code, keeping the compiler gcc?
The full compiler error, within the 'make' compilation
mpif90 -cpp -O3 -ffast-math -ffree-line-length-none -fopenmp -fmax-errors=4 -march=native -DMPI -DEFTCOSMOMC -I../EFTCAMB/ReleaseEFTMPI -JReleaseEFTMPI -IReleaseEFTMPI/ -c CosmoTheory.f90 -o ReleaseEFTMPI/CosmoTheory.o
CosmoTheory.f90:253:52:
read(F%Unit) FileSettings%TCosmoTheoryParams
1
Error: Data transfer element at (1) cannot have ALLOCATABLE components unless it is processed by a defined input/output procedure
CosmoTheory.f90:183:54:
Write(F%Unit) CosmoSettings%TCosmoTheoryParams
1
Error: Data transfer element at (1) cannot have ALLOCATABLE components unless it is processed by a defined input/output procedure
And the snippet of code where these lines are from:
subroutine TCosmoTheoryPredictions_ReadTheory(this, F, first)
Class(TCosmoTheoryPredictions) this
class(TFileStream) :: F
logical, intent(in) :: first
type(TCosmoTheorySettings), save :: FileSettings
!JD 02/14 new variables for handling new pk arrays
integer :: num_k, num_z
real(mcp), allocatable :: temp(:,:)
real(mcp), allocatable :: k(:), z(:)
real(mcp), allocatable :: cl(:)
real(mcp), allocatable :: valArray(:)
integer i,j
if (first) then
read(F%Unit) FileSettings%TCosmoTheoryParams
if (FileSettings%use_LSS) call F%ReadSizedArray(FileSettings%power_redshifts)
if (FileSettings%use_CMB) call F%ReadSizedArray(FileSettings%cl_lmax)
call F%ReadSizedArray(FileSettings%ArraySizes) !not used
end if
subroutine TCosmoTheoryPredictions_WriteTheory(this, F, first)
Class(TCosmoTheoryPredictions) this
class(TFileStream) :: F
logical, intent(in) :: first
integer ArraySizes(1)
real(mcp) :: valArray(6)
integer i,j
if (first .and. new_chains) then
Write(F%Unit) CosmoSettings%TCosmoTheoryParams
if (CosmoSettings%use_LSS) call F%WriteSizedArray(CosmoSettings%power_redshifts)
if (CosmoSettings%use_CMB) call F%WriteSizedArray(CosmoSettings%cl_lmax)
ArraySizes(1)=size(valArray)
call F%WriteSizedArray(ArraySizes)
end if
Definition of TCosmoTheorySettings:
Type, extends(TCosmoTheoryParams):: TCosmoTheorySettings
!Just add the allocatable components
integer, allocatable :: cl_lmax(:,:)
integer, allocatable :: ArraySizes(:)
!e.g. lmax_cl(1,1) is lmax for TT; zero if CL is not used; order is T, E, B, Phi
real(mcp), dimension(:), allocatable :: power_redshifts
contains
procedure, private :: Initialize_PKSettings
procedure, private :: Initialize_CMBSettings
procedure :: InitForLikelihoods => TCosmoTheorySettings_InitForLikelihoods
procedure :: ReadParams => TCosmoTheorySettings_ReadParams
end type TCosmoTheorySettings
Type TCosmoTheoryParams contains only ordinary non-allocatable and non-pointer data components and no procedures

Fortran: (mis)matching dynamic types

I am copying a variable of certain class in another of the same class. The compiler happily compiles this but I am worried that at run time the dynamic types may differ. Do I need to test that the two objects are of the same dynamic type to prevent copying rectangle in a square say or may I trust the compiler? What happens if a rectangle is copied in a square accidentally?
What I am trying to do is the following:
type :: simVars
class(stateVars), dimension(:), allocatable :: svars
integer :: count_
contains
procedure :: init => init_simVars
procedure :: destroy => dest_simVars
procedure :: add => add_to_simVars ! adds an observation to the time series
end type simVars
subroutine init_simVars(this,n)
!--> VERSION 1
class(simVars), intent(inout) :: this
integer, intent(in) :: n
allocate( this%svars(n) )
this%count_ = 0
end subroutine init_simVars
subroutine init_simVars(this,n,sVarsIni)
!--> VERSION 2
class(simVars), intent(inout) :: this
integer, intent(in) :: n
class(stateVars), intent(in) :: sVarsIni
allocate( this%svars(n),source=sVarsIni )
this%count_ = 0
end subroutine init_simVars
subroutine add_to_simvars(this,svars)
class(simVars), intent(inout) :: this
class(stateVars), intent(in) :: svars
this%count_ = this%count_+1
this%svars(this%count_) = svars
end subroutine add_to_simvars
subroutine doSimulation(simHist,sVarsIni)
class(simVars), intent(out) :: simHist
class(stateVars), intent(in) :: sVarsIni
!--> dynamic type 'stateVars1'
class(stateVars), allocatable :: sVars ! will be source allocated from 'iniState'
! initialize the state of the economy
allocate( sVars, source=sVarsIni ) ! "copies" 'sVarsIni' in 'sVars'
! initialize 'simHist'
!--> VERSION 1:
call simHist%init(nYears)
!--> VERSION 2:
call simHist%init(nYears,iniState)
! save today's variables
call simHist%add(sVars)
...
end subroutine doSimulation
Compiler (ifort 14) happily compiles both versions but I strongly suspect that VERSION 1 is wrong. In init_simVars this%svars will be allocated to dynamic type stateVars, in add_to_simvars sVars will have dynamic type stateVars1 and a copy in this%sVars (of type stateVars) will be attempted. I am quite surprised that the compiler compiles this even though it cannot determine the dynamic type of sVars in add_to_simvars. What would happen at run time, a seg fault or what?
VERSION 2 I believe is correct however I am somewhat reluctant to trust the compiler here therefore I am thinking I should ASSERT that this%sVars and sVars have the same dynamic type (ASSERT(SAME_TYPE_AS(this%sVars, sVars) ))? Is this a real concern or am I worried too much?
Another question is what happens when I do allocate( this%svars(n),source=sVarsIni ). I want to allocate the array this%sVars to be of size n and dynamic type sVarsIni. However sVarsIni is a scalar. Will it do what I want?
The difference is in these lines
allocate( this%svars(n) )
vs.
allocate( this%svars(n),source=sVarsIni )
where this%svars is class(svars) allocatable array and svarsIni is an class(stateVars) dummy argument.
That indeed changes a lot.
In the first case, it allocates it to the declared type, which is svars, in the other case it allocates to the dynamic type of the dummy argument, which is at least stateVars.
If you do version 1, it should than fail at add_to_simvars, because the dynamic types won't match.
I don't know if you overloaded the assignment in there or not. If you didn't it shouldn't even compile, because of the intrinsic assignment of polymorphic objects.

Memory trouble when deallocating Fortran linked list,

I have implemented a generic linked list (genII.f90) written in Fortran found in
http://fortranwiki.org/fortran/show/Linked+list .
I test it and everything is ok except the fact that the LI_Remove_Head function seems not to free the memory.
I add to the original module the function LI_Destruct (see below) and same result it does not free the memory.
SUBROUTINE LI_Destruct(List)
implicit none
TYPE(List_Type),INTENT(INOUT),TARGET :: List
TYPE(Link_Ptr_Type) :: Link_current, Link_next
Link_next%P =>List%Head%next
do while (associated(Link_next%P))
Link_current%P => Link_next%P
Link_next%P => Link_next%P%next
deallocate(Link_current%P)
end do
end subroutine LI_destruct
I certainly miss something so my questions are two:
1- Is there an error in the code? For what reason the memory is not emptied by the "deallocate"?
2- Does it exist better and almost standard generic linked list for fortran?
I add the simple code below used to do the test:
PROGRAM test_list
! Defines data and other list(s) and arrays for particles.
USE Generic_List, ONLY : Link_Ptr_Type,Link_Type,List_Type
USE Generic_List, ONLY : LI_Init_List,LI_Add_To_Head,LI_Add_To_Tail,LI_Get_Head,&
LI_Remove_Head,LI_Get_Next,LI_Associated,LI_Get_Len, LI_destruct
IMPLICIT NONE
TYPE:: Particle_data
REAL, dimension(2) :: pos !! Coordinate dimensionali
END TYPE Particle_data
! Definition of the types necessary for the list
TYPE Particle_Node
TYPE(Link_Type) :: Link
TYPE(Particle_data), pointer :: Data
END TYPE Particle_Node
TYPE Particle_Node_ptr
TYPE(Particle_Node), pointer :: P
END TYPE Particle_Node_ptr
! Create array of lists in order to allow classify the particles
TYPE(List_Type), allocatable :: ao_Particle_List(:)
TYPE(Link_Ptr_Type) :: Link
TYPE(Particle_Node_ptr) :: Particle_elem
!-------------------------------------------------------------!
!-------------------------------------------------------------!
INTEGER, parameter :: Npart_test = 1000000 ! , nPart
INTEGER :: i,iter,j,item,nBuffer
REAL :: pos(2)
nBuffer = 5
IF (ALLOCATED(ao_Particle_List)) DEALLOCATE(ao_Particle_List)
ALLOCATE(ao_Particle_List(0:nBuffer))
! Init list used for temporary construction
DO iter=0,nBuffer
CALL LI_Init_List(ao_Particle_List(iter))
ENDDO
DO j=1,NBuffer
DO i=1,Npart_test
pos(1)=i*1.0; pos(2)=j*i
ALLOCATE(Particle_elem%P); ALLOCATE(Particle_elem%P%Data) ! Allocate data before store
Particle_elem%P%Data%pos = pos
! Elem is treated and should be put at head of the list ao_Particle_List(item)
item=j
Link = TRANSFER(Particle_elem,Link); CALL LI_Add_To_Head(Link,ao_Particle_List(item)) ! STORAGE
END DO
END DO
WRITE(*,*) "List is full, see RAM"; READ(*,*)
! Write(*,*) "Destruct list"
DO iter=0,nBuffer
CALL LI_Destruct(ao_Particle_List(iter))
ENDDO
IF (ALLOCATED(ao_Particle_List)) DEALLOCATE(ao_Particle_List)
WRITE(*,*) "List is empty, see RAM"; READ(*,*)
END PROGRAM
Thanks to all,
John
OK the problem is solved. As there is a transfer operation it seems that the data cannot be reached in the function LI_DESTRUCT done.
The correct way to deallocate the linked list is (slightly different as the example in fortran wiki) is:
DO
Link = LI_Remove_Head(ao_Particle_List)
IF(.NOT.LI_Associated(Link))EXIT
User = TRANSFER(Link,User)
!~ WRITE(6,*)User%P%Data%Index,User%P%Data%User_Stuff
DEALLOCATE(User%P%Data)
DEALLOCATE(User%P)
ENDDO

changing array dimensions in fortran

There are basically two ways to pass arrays to a subroutine in Fortran 90/95:
PROGRAM ARRAY
INTEGER, ALLOCATABLE :: A(:,:)
INTEGER :: N
ALLOCATE(A(N,N))
CALL ARRAY_EXPLICIT(A,N)
! or
CALL ARRAY_ASSUMED(A)
END PROGRAM ARRAY
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N,N)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
SUBROUTINE ARRAY_ASSUMED(A)
INTEGER, ALLOCATABLE :: A(:,:)
N=SIZE(A,1)
! bla bla
END SUBROUTINE ARRAY_ASSUMED
where you need an explicit interface for the second, usually through the use of a module.
From FORTRAN77, I'm used to the first alternative, and I read this is also the most efficient if you pass the whole array.
The nice thing with the explicit shape is that I can also call a subroutine and treat the array as a vector instead of a matrix:
SUBROUTINE ARRAY_EXPLICIT(A,N)
INTEGER :: N
INTEGER :: A(N**2)
! bla bla
END SUBROUTINE ARRAY_EXPLICIT
I wondered if there is a nice way to do that kind of thing using the second, assumed shape interface, without copying it.
See the RESHAPE intrinsic, e.g.
http://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html
Alternatively, if you want to avoid the copy (in some cases an optimizing compiler might be able to do a reshape without copying, e.g. if the RHS array is not used afterwards, but I wouldn't count on it), as of Fortran 2003 you can assign pointers to targets of different rank, using bounds remapping. E.g. something like
program ptrtest
real, pointer :: a(:)
real, pointer :: b(:,:)
integer :: n = 10
allocate(a(n**2))
a = 42
b (1:n, 1:n) => a
end program ptrtest
I was looking to do the same thing and came across this discussion. None of the solutions suited my purposes, but I found that there is a way to reshape an array without copying the data using iso_c_binding if you are using the fortran 2003 standard which current fortran 90/95 compilers tend to support. I know the discussion is old, but I figured I would add what I came up with for the benefit of others with this question.
The key is to use the function C_LOC to convert an array to an array pointer, and then use C_F_POINTER to convert this back into a fortran array pointer with the desired shape. One challenge with using C_LOC is that C_LOC only works for array that have a directly specified shape. This is because arrays in fortran with an incomplete size specification (i.e., that use a : for some dimension) include an array descriptor along with the array data. C_LOC does not give you the memory location of the array data, but the location of the descriptor. So an allocatable array or a pointer array don't work with C_LOC (unless you want the location of the compiler specific array descriptor data structure). The solution is to create a subroutine or function that receives the array as an array of fixed size (the size really doesn't matter). This causes the array variable in the function (or subroutine) to point to the location of the array data rather than the location of the array descriptor. You then use C_LOC to get a pointer to the array data location and C_F_POINTER to convert this pointer back into an array with the desired shape. The desired shape must be passed into this function to be used with C_F_POINTER. Below is an example:
program arrayresize
implicit none
integer, allocatable :: array1(:)
integer, pointer :: array2(:,:)
! allocate and initialize array1
allocate(array1(6))
array1 = (/1,2,3,4,5,6/)
! This starts out initialized to 2
print *, 'array1(2) = ', array1(2)
! Point array2 to same data as array1. The shape of array2
! is passed in as an array of intergers because C_F_POINTER
! uses and array of intergers as a SIZE parameter.
array2 => getArray(array1, (/2,3/))
! Change the value at array2(2,1) (same as array1(2))
array2(2,1) = 5
! Show that data in array1(2) was modified by changing
! array2(2,1)
print *, 'array(2,1) = array1(2) = ', array1(2)
contains
function getArray(array, shape_) result(aptr)
use iso_c_binding, only: C_LOC, C_F_POINTER
! Pass in the array as an array of fixed size so that there
! is no array descriptor associated with it. This means we
! can get a pointer to the location of the data using C_LOC
integer, target :: array(1)
integer :: shape_(:)
integer, pointer :: aptr(:,:)
! Use C_LOC to get the start location of the array data, and
! use C_F_POINTER to turn this into a fortran pointer (aptr).
! Note that we need to specify the shape of the pointer using an
! integer array.
call C_F_POINTER(C_LOC(array), aptr, shape_)
end function
end program
#janneb has already answered re RESHAPE. RESHAPE is a function -- usually used in an assignment statement so there will be a copy operation. Perhaps it can be done without copying using pointers. Unless the array is huge, it is probably better to use RESHAPE.
I'm skeptical that the explicit shape array is more efficient than the assumed shape, in terms of runtime. My inclination is to use the features of the Fortran >=90 language and use assumed shape declarations ... that way you don't have to bother passing the dimensions.
EDIT:
I tested the sample program of #janneb with ifort 11, gfortran 4.5 and gfortran 4.6. Of these three, it only works in gfortran 4.6. Interestingly, to go the other direction and connect a 1-D array to an existing 2-D array requires another new feature of Fortran 2008, the "contiguous" attribute -- at least according to gfortran 4.6.0 20110318. Without this attribute in the declaration, there is a compile time error.
program test_ptrs
implicit none
integer :: i, j
real, dimension (:,:), pointer, contiguous :: array_twod
real, dimension (:), pointer :: array_oned
allocate ( array_twod (2,2) )
do i=1,2
do j=1,2
array_twod (i,j) = i*j
end do
end do
array_oned (1:4) => array_twod
write (*, *) array_oned
stop
end program test_ptrs
You can use assumed-size arrays, but it can mean multiple layers of wrapper
routines:
program test
implicit none
integer :: test_array(10,2)
test_array(:,1) = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
test_array(:,2) = (/11, 12, 13, 14, 15, 16, 17, 18, 19, 20/)
write(*,*) "Original array:"
call print_a(test_array)
write(*,*) "Reshaped array:"
call print_reshaped(test_array, size(test_array))
contains
subroutine print_reshaped(a, n)
integer, intent(in) :: a(*)
integer, intent(in) :: n
call print_two_dim(a, 2, n/2)
end subroutine
subroutine print_two_dim(a, n1, n2)
integer, intent(in) :: a(1:n1,1:*)
integer, intent(in) :: n1, n2
call print_a(a(1:n1,1:n2))
end subroutine
subroutine print_a(a)
integer, intent(in) :: a(:,:)
integer :: i
write(*,*) "shape:", shape(a)
do i = 1, size(a(1,:))
write(*,*) a(:,i)
end do
end subroutine
end program test
I am using ifort 14.0.3 and 2D to 1D conversion, I could use an allocatable array for 2D array and a pointer array for 1D:
integer,allocatable,target :: A(:,:)
integer,pointer :: AP(:)
allocate(A(3,N))
AP(1:3*N) => A
As #M.S.B mentioned, in case both A and AP have the pointer attribute, I had to use contiguous attribute for A to guarantee the consistency of the conversion.
Gfortran is a bit paranoid with interfaces. It not only wants to know the type, kind, rank and number of arguments, but also the shape, the target attribute and the intent (although I agree with the intent part). I encountered a similar problem.
With gfortran, there are three different dimension definition:
1. Fixed
2. Variable
3. Assumed-size
With ifort, categories 1 and 2 are considered the same, so you can do just define any dimension size as 0 in the interface and it works.
program test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(sz,arr)
integer, intent(in) :: sz
integer, dimension(0), intent(in) :: arr
! This zero means that the size does not matter,
! as long as it is a one-dimensional integer array.
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(3,ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(3,ownlist)
deallocate(ownlist)
contains
! This one has a dimension size as input.
subroutine rout1(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(sz), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
! This one has a fixed dimension size.
subroutine rout2(sz,arr)
implicit none
integer, intent(in) :: sz
integer, dimension(0:10), intent(in) :: arr
write(*,*) "Ignored integer: ",sz
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program test
Gfortran complains about the interface. Changing the 0 into 'sz' solves the problem four 'rout1', but not for 'rout2'.
However, you can fool gfortran around and say dimension(0:10+0*sz) instead of dimension(0:10) and gfortran compiles and gives the same
result as ifort.
This is a stupid trick and it relies on the existence of the integer 'sz' that may not be there. Another program:
program difficult_test
implicit none
integer, dimension(:), allocatable :: ownlist
interface
subroutine blueprint(arr)
integer, dimension(0), intent(in) :: arr
end subroutine blueprint
end interface
procedure(blueprint), pointer :: ptr
allocate(ownlist(3))
ownlist = (/3,4,5/)
ptr => rout1
call ptr(ownlist)
deallocate(ownlist)
allocate(ownlist(0:10))
ownlist = (/3,4,5,6,7,8,9,0,1,2,3/)
ptr => rout2
call ptr(ownlist)
deallocate(ownlist)
contains
subroutine rout1(arr)
implicit none
integer, dimension(3), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout1
subroutine rout2(arr)
implicit none
integer, dimension(0:10), intent(in) :: arr
write(*,*) arr
write(*,*) arr(1)
end subroutine rout2
end program difficult_test
This works under ifort for the same reasons as the previous example, but gfortran complains about the interface. I do not know how I can fix it.
The only thing I want to tell gfortran is 'I do not know the dimension size yet, but we will fix it.'. But this needs a spare integer arguemnt (or something else that we can turn into an integer) to fool gfortran around.