Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

A component cannot be an array if the encompassing structure is an array.

Julian_H_
Beginner
1,208 Views

I have the following problem:

I need to create an datasets array which size is not known at first. It is an composition of different subarrays. 
For example the first subdataset has 4 elements, the second 10, and a third 8. Because this sizes are  not known at the start of the programm I can not do somethin like:

 

INTEGER(4), ALLOCATE :: datasets(:)

ALLOCATE(datasets(1:4+10+8))

The sizes of each subdataset is evaluated one after each other. Because there is no such thing like appending an array to an array I would have to reallocate the datasets size after each subdataset to manually expend the array. But this always involves a copy process which I definetly have to avoid.

So I tried to use an array of pointers in which every pointer points to an allocatable array. This works.

P1  | P2  | P3
1   |   2  |  3
1   |   2  |  3
1   |   2  |  3
1   |   2  |  3
    |   2  |  3
    |   2  |  3
    |   2  |  3
    |   2  |  3 
    |   2  |  
    |   2  |  

Its getting tricky now, because I have a subroutine  (sub1) which needs one array. Is there any possibility to pass the data inside the subroutine without copying it?

1  | 1  | 1  | 1  | 2  |  2  |  2  |  2  |  2  |  2  |  2  |  2  |  2  |  2  |  3  |  3  |  3  |  3  |  3  |  3  |  3  |  3  | 
MODULE TEST_MOD
	IMPLICIT NONE
	
	PRIVATE	
	
	! Type
	TYPE :: TYPE_TESTDATA
		INTEGER(4), ALLOCATABLE :: data(:)
	END TYPE TYPE_TESTDATA
		
	TYPE :: TYPE_TEST_PTR
		TYPE(TYPE_TESTDATA), POINTER :: p
	END TYPE TYPE_TEST_PTR
		
	CONTAINS
	
	! Subroutines
	SUBROUTINE sub1(array)
		IMPLICIT NONE
		INTEGER(4), ALLOCATABLE, INTENT(IN) :: array( : )
		
		! do smth with array
	
	END SUBROUTINE sub1
	
	
	SUBROUTINE sub2()
		IMPLICIT NONE
		
		INTEGER(4)							:: size_data1, size_data2, size_data3
		INTEGER(4)							:: num_datasets
		TYPE(TYPE_TEST_PTR), ALLOCATABLE	:: datasets(:)
		
		num_datasets = 3
		size_data1 = 4
		size_data2 = 10
		size_data3 = 8
		
		! allocate 
		ALLOCATE(datasets(1:num_datasets))
		ALLOCATE(datasets(1)%p%data(1:size_data1))
		ALLOCATE(datasets(2)%p%data(1:size_data2))
		ALLOCATE(datasets(3)%p%data(1:size_data3))
		
		datasets(1)%p%data = 1
		datasets(2)%p%data = 2
		datasets(3)%p%data = 3
		
		CALL sub1(datasets(:)%p%data)
	
	END SUBROUTINE sub2
END MODULE TEST_MOD

I hope I have described the problem quite understandably. Thanks for your help in advance.

0 Kudos
8 Replies
FortranFan
Honored Contributor II
1,208 Views

Why can't you simply have an array of your first type, a container, that holds the data array?  Here's a simple example with floating-point data, it should give you the idea:

   type :: data_t
      real, allocatable :: data(:)
   end type

   type(data_t), allocatable :: datasets(:)
   integer :: i
   integer :: j

   allocate( datasets(3) )
   allocate( datasets(1)%data(4) )
   allocate( datasets(2)%data(10) )
   allocate( datasets(3)%data(8) )

   do i = 1, size(datasets)
      call sub( datasets(i)%data )
      print "(g0,i1,g0)", "datasets(",i,")%data = "
      do j = 1, size(datasets(i)%data)
         print *, datasets(i)%data(j)
      end do
   end do

contains

   subroutine sub( dat )

      real, intent(inout) :: dat(:)

      call random_number( dat )

   end subroutine

end
datasets(1)%data =
  0.282871902
  0.285217583
  0.948108971
  0.329367697
datasets(2)%data =
   7.51177669E-02
   4.24366593E-02
   3.02464366E-02
  0.472927332
  0.843287349
  0.165476203
   7.99026489E-02
  0.889786005
  0.139770269
  0.276428401
datasets(3)%data =
  0.226669431
  0.373131812
  0.915089190
  0.754180670
  0.162718296
  0.675101459
  0.131620944
  0.682734549

 

0 Kudos
Julian_H_
Beginner
1,208 Views

Sure this is possible. But its no solving the actual problem. I need to pass the complete dataset not just a subdataset.

 

CALL sub1(datasets(:)%p%data)

not

CALL sub1(datasets(1)%p%data)

 

0 Kudos
andrew_4619
Honored Contributor II
1,208 Views

Julian H. wrote:
Sure this is possible. But its no solving the actual problem. I need to pass the complete dataset not just a subdataset.

CALL sub1(datasets(:)%p%data)

not

CALL sub1(datasets(1)%p%data)

i am really not sure what you are getting at here, if you are passing the whole dataset that would be:

CALL sub1(datasets)

you need to put the type declaration in a module so you can USE it in sub1 to declare the dummy arg being passed in.

 

0 Kudos
FortranFan
Honored Contributor II
1,208 Views

Julian H. wrote:

Sure this is possible. But its no solving the actual problem. I need to pass the complete dataset not just a subdataset. ..

You may want to consult some helpful references on Fortran: https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world

 

0 Kudos
Julian_H_
Beginner
1,208 Views

andrew_4619 wrote:

Quote:

Julian H. wrote:
Sure this is possible. But its no solving the actual problem. I need to pass the complete dataset not just a subdataset.

 

CALL sub1(datasets(:)%p%data)

not

CALL sub1(datasets(1)%p%data)

 

i am really not sure what you are getting at here, if you are passing the whole dataset that would be:

CALL sub1(datasets)

you need to put the type declaration in a module so you can USE it in sub1 to declare the dummy arg being passed in.

 

yes correct. If i want to have this type in sub1. But i dont want because it makes the algorithm behind it much more complicated. What I need in in sub1 is an normal integer, allocatable array. So by passing the dataset inside I was talking about the data in general not the type datasets.

But what I have in sub2, which is the function collecting the data for sub1, is the shown type. Why am I using this type and not a normal integer array in sub2 I tried to explain in my initial post.

So I am looking for a solution to transfer the data into sub1 or how i can collect the data in sub2 in a different way, that i can just pass a normal integer array.

 

 

0 Kudos
andrew_4619
Honored Contributor II
1,208 Views

Well if you want a POD (plain old data) array in sub1 then go back to the old way of things and make data a large enough array and then as you populate it fill an index array  such that   ipos(i) the the start position of the i th sub array and its length is ipos(i+1)- ipos(i)  

0 Kudos
Julian_H_
Beginner
1,208 Views

andrew_4619 wrote:

Well if you want a POD (plain old data) array in sub1 then go back to the old way of things and make data a large enough array and then as you populate it fill an index array  such that   ipos(i) the the start position of the i th sub array and its length is ipos(i+1)- ipos(i)  

I guess thats what I have to do if I am not able to change the algorithm. Thanks for your help.

0 Kudos
IanH
Honored Contributor II
1,208 Views

Perhaps also challenge the "definitely avoid" aspect of copying the array data.  Collect the data into an array of objects of type similar to your TYPE_TESTDATA type (or a linked list of similar objects), then flatten the data into a single allocatable array prior to calling the subroutine.

0 Kudos
Reply