equivalent of SIZE for data structure - fortran

I have a data structure like :
AllPaths%list_Pw(A,B)%vectors(C)%elements(D, E)
But for each pair of values A,B the length of c is different. And the length of D,E is different for each value of C.
The SIZE function work only for array but is there an equivalent for data structure, like:
Cmax=SIZElike(AllPaths%list_Pw(A,B)%vectors)
The alternative would be keeping the size of each vectors for each pair of A,B when I build my data structure, but I would be one more big array, with lot of information that I will not need...
EDIT
For example my Amax=5 and Bmax=5
I would like to be able to know what is Cmax for A=4 and B=3
AllPaths%list_Pw(4,3)%vectors(Cmax)
Because this Cmax would be different if A=2 and B=4 :
AllPaths%list_Pw(2,4)%vectors(Cmax)
EDIT2
How types are declared :
MODULE NewType
TYPE :: vector
INTEGER, DIMENSION(:, :), ALLOCATABLE :: elements
END TYPE vector
TYPE :: ragged_array
TYPE(vector), DIMENSION(:), ALLOCATABLE :: vectors
END TYPE ragged_array
TYPE :: List_RA
TYPE(ragged_array),DIMENSION(:, :),ALLOCATABLE::List_PW
END TYPE List_RA
END MODULE

Related

Generalizing your operation for a specific declared type in Fortran

I have a Structure of arrays using the declared type in Fortran
e.g.
type t_data
integer :: N
real, allocatable :: x(:)
real, allocatable :: z(:)
real, allocatable :: y(:)
contains
procedure :: copy
procedure :: SWAP
procedure :: copy_element
end type
! constructor
interface t_data
module procedure constructor
end interface
contains
subroutine copy(this, old)
class(t_data), intent(inout) :: this
type(t_data), intent(in) :: old
do i = 1, old% N
this% x(i) = old% x(i)
etc ..
end do
end subroutine
subroutine copy(this, old)
class(t_data), intent(inout) :: this
type(t_data), intent(in) :: old
do i = 1, old% N
this% x(i) = old% x(i)
etc ..
end do
end subroutine
function constructor(size_)
integer, intent(in) :: size_
type(t_data), :: constructor
allocate(constructor% x(size_))
allocate(constructor% y(size_) )
! etc
end function
subroutine swap(this, from, i1,i2)
class(t_particle_data), intent(inout) :: this
type(t_particle_data), intent(in) :: from
integer, intent(in) :: i1, i2
this% x(i1) = from% x(i2)
! etc
end subroutine
These are a set of examples of procedures that need to do same operations on all arrays of the declared type t_data. My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type.
Currently, when I add a new array to my t_data, I need to go through all those procedures, constructors, deconstructors, and add the component.
I am asking if there is a way to make this more easier.
MY APPLICATION
Please note that these data type is used for particle simulation. Initially I allocate t_data with a large number. However, later during my simulation I might need more particles. Hence, I allocate a new t_data with more memory and copy over the old t_data up to its old size.
subroutine NEW_ALLOC(new, old)
type(t_data), intent(out) :: new
type(t_data), intent(inout) :: old
nsize = old% N * 2 ! allocate twice the old size
new = T_DATA(nsize)
call new% copy(old)
!DEALLCOte OLD
end subroutine
Does anybody has/is it possible to make this in a more clever way. I do not mind mixing this with C/C++?
My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type.
Here's how I would tackle the situation, and how many Fortran programmers have tackled the situation. I don't see the compelling need to have a derived type containing 3 arrays of coordinates, and approaching the problem that way does, as OP fears, require that adding another dimension to the problem requires code revision, such as adding a member array real, allocatable :: w(:) to t_data and recoding all the type-bound procedures operating on the type.
So drop that approach in favour of
TYPE t_data
REAL, DIMENSION(:,:), ALLOCATABLE :: elements
END TYPE t_data
let's have a couple of instances for exposition
TYPE(t_data) :: t1 ,t2, t3
and we can allocate the elements member of any of these this way
ALLOCATE(t1%elements(3,10))
which could just as easily be
ALLOCATE(t1%elements(6,100))
or whatever you wish. This has the advantage over the original derived type design that the dimensions of elements can be determined at run-time. It also makes it difficult to have different lengths for each of the coordinate arrays.
Now, copying t1 is as simple as
t2 = t1
Modern Fortran even takes care of automatically allocating the elements of t2. So I don't see any need for defining procedures for copying whole instances of t_data. As for swapping data around, slicing and dicing, this is as simple as
t2%elements(1,:) = t1%elements(2,:)
even
t2%elements(1,:) = t1%elements(1,6:1:-1)
or
t2%elements(1,:) = t1%elements(1,[1,3,5,2,4,6])
It should be obvious how to wrap these into a swap routine. But if not, ask another question.
Next, to the point about needing to allocate more space for elements during execution. First a temporary array
REAL, DIMENSION(:,:), ALLOCATABLE :: temp
then a little code like this, to double the size of elements.
ALLOCATE(temp(3,2*n))
temp(:,1:n) = t2%elements(:,1:n)
CALL MOVE_ALLOC(to=t2%elements,from=temp)
Again, you might care to wrap this into a procedure and if you need help doing that, ask for it.
Finally, the lesson of all this is not to share how I would program the problem, but to share the idea to program in Fortran.

2D dynamic array allocation [duplicate]

I am trying to make an array with different lengths in a second dimension e.g.:
A = 1 3 5 6 9
2 3 2
2 5 8 9
Is this possible? I've spent a fair amount of time looking but cannot find out either way.
Yes and no. First the no:
Proper arrays in Fortran, such as those declared like this:
integer, dimension(3,3,4) :: an_array
or like this
integer, dimension(:,:,:,:), allocatable :: an_array
are regular; for each dimension there is only one extent.
But, if you want to define your own type for a ragged array you can, and it's relatively easy:
type :: vector
integer, dimension(:), allocatable :: elements
end type vector
type :: ragged_array
type(vector), dimension(:), allocatable :: vectors
end type ragged_array
With this sort of approach you can allocate the elements of each of the vectors to a different size. For example:
type(ragged_array) :: ragarr
...
allocate(ragarr%vectors(5))
...
allocate(ragarr%vectors(1)%elements(3))
allocate(ragarr%vectors(2)%elements(4))
allocate(ragarr%vectors(3)%elements(6))
looking at the first answer, it seems there is no need to create the derived type vector which is really just an allocatable integer array:
type ragged_array
integer,allocatable::v(:)
end type ragged_array
type(ragged_array),allocatable::r(:)
allocate(r(3))
allocate(r(1)%v(5))
allocate(r(2)%v(10))
allocate(r(3)%v(15))
this makes the notation a little less cumbersome..

Declaration of 2D array dimension - first dimension must be the one element from 1D array [duplicate]

I am trying to make an array with different lengths in a second dimension e.g.:
A = 1 3 5 6 9
2 3 2
2 5 8 9
Is this possible? I've spent a fair amount of time looking but cannot find out either way.
Yes and no. First the no:
Proper arrays in Fortran, such as those declared like this:
integer, dimension(3,3,4) :: an_array
or like this
integer, dimension(:,:,:,:), allocatable :: an_array
are regular; for each dimension there is only one extent.
But, if you want to define your own type for a ragged array you can, and it's relatively easy:
type :: vector
integer, dimension(:), allocatable :: elements
end type vector
type :: ragged_array
type(vector), dimension(:), allocatable :: vectors
end type ragged_array
With this sort of approach you can allocate the elements of each of the vectors to a different size. For example:
type(ragged_array) :: ragarr
...
allocate(ragarr%vectors(5))
...
allocate(ragarr%vectors(1)%elements(3))
allocate(ragarr%vectors(2)%elements(4))
allocate(ragarr%vectors(3)%elements(6))
looking at the first answer, it seems there is no need to create the derived type vector which is really just an allocatable integer array:
type ragged_array
integer,allocatable::v(:)
end type ragged_array
type(ragged_array),allocatable::r(:)
allocate(r(3))
allocate(r(1)%v(5))
allocate(r(2)%v(10))
allocate(r(3)%v(15))
this makes the notation a little less cumbersome..

Index multiple non-adjacent elements of a Fortran array

Is there a way in Fortran to access many elements of an array without using a loop?
For example given array of 100 elements
real(100) :: a
can I do something like this to access elements 1,4,7,54,81 that do not follow a regular step?
a(1,4,7,54,81)= 3.21423
you could use a vector subscript: a( (/1,4,7,54,81/) )= 3.21423
As noted before, an array may be used as the indexes of an array. This is a so-called vector subscript.
A([1,4,7,54,81]) = 3.21423
sets the elements given to that value. (This is the same as the earlier answer but using the Fortran 2003+/modern array constructor notation.)
The array can be any rank-1 array, such as a variable or expression:
integer :: idx(5)=[1,4,7,54,81]
A(idx) = 3.21423
A(idx-1+1) = 3.21423
Of course, vector subscripts are of use in other settings, such as referencing:
print *, A(idx)
call sub(A(idx))
A(idx) = A(idx+1) + 5
However, array sections with vector subscripts are subject to various restrictions, such as:
not always may they be arguments to a procedure;
a pointer may not point to them;
not all such sections may be assigned to.
In the third case, if the same index appears more than once in the subscript we can't define it. So
print *, A([1,5,1])
is allowed, but
A([1,5,1]) = 3.
is not.
RESHAPE and WHERE are worth looking at.
If you are determining which elements to 'pull out' then maybe ALLOCATE a new variable and stuff the elements of A into B.
Maybe something like this:
REAL, DIMENSION(100) :: A
LOGICAL, DIMENSION(100) :: A_Mask
INTEGER :: SizeB
REAL, DIMENSION(:), ALLOCATABLE :: B
!...
A_Mask = .FALSE.
WHERE(A > 1.0)
A_Mask = .TRUE.
ENDWHERE
SizeB = SUM(A_Mask)
!... then allocate B and fill it.

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.