How to increase array size on-the-fly in Fortran?
Asked Answered
W

4

14

My program is running though 3D array, labelling 'clusters' that it finds and then doing some checks to see if any neighbouring clusters have a label higher than the current cluster. There's a second array that holds the 'proper' cluster label. If it finds that the nth adjoining cluster is labelled correctly, that element is assigned to 0, otherwise is assigns it to the correct label (for instance if the nth site has label 2, and a neighbour is labeled 3, the 3rd element of the labelArray is set to 2). I've got a good reason to do this, honest!

All I want is to be able to assign the nth element of the labelArray on the fly. I've looked at allocatable arrays and declaring things as labelArray(*) but I don't really understand these, despite searching the web, and StackOverflow.

So any help on doing this would be awesome.

Windbound answered 5/12, 2011 at 11:11 Comment(0)
E
14

Here is a Stack Overflow question with some code examples showing several ways of using Fortran allocatable arrays: How to get priorly-unkown array as the output of a function in Fortran: declaring, allocating, testing for being already being allocated, using the new move_alloc and allocation on assignment. Not shown there is explicit deallocation, since the examples are using move_alloc and automatic deallocation on exit of a procedure.

P.S. If you want to repeatedly add one element you should think about your data structure approach. Adding one element at a time by growing an array is not an efficient approach. To grow an array from N elements to N+1 in Fortran will likely mean creating a new array and copying all of the existing elements. A more appropriate data structure might be a linked list. You can create a linked list in Fortran by creating a user-defined type and using pointers. You chain the members together, pointing from one to the next. The overhead to adding another member is minor. The drawback is that it is easiest to access the members of the list in order. You don't have the easy ability of an array, using indices, to access the members in any order.

Info about linked lists in Fortran that I found on the web: http://www-uxsup.csx.cam.ac.uk/courses/Fortran/paper_12.pdf and http://www.iag.uni-stuttgart.de/IAG/institut/abteilungen/numerik/images/4/4c/Pointer_Introduction.pdf

Ellsworth answered 5/12, 2011 at 11:50 Comment(8)
So looking at what @Vladimir wrote, and your link, is this acceptable code? allocate(temp(size(labelArray)+1))// temp(:size(labelArray))=labelArray// call move_alloc(temp,labelArray)Windbound
That looks correct. If the array size is large it may be inefficient because of the copy caused by the assignment statement.Ellsworth
Ok, When I ran it with an oversized array there weren't that many actual elements. I'm restricted to using fortran90 however, and I didn't think it had pointers?Windbound
Fortran 90 has pointers. If you are limited to Fortran 90 (that's a long time ago!), then you won't be able to use move_alloc since that is a Fortran 2003 addition. The replacement is to deallocate labelArray, allocate labelArray to the new size, assign to LabelArray, then deallocate temp.Ellsworth
I put in the move_alloc code and the compile didn't shout at me. I've put in the deallocation code now and it still doesn't shout. I need to fix an array-assignment issue before I know it worked though.Windbound
I'm now getting a segfault at a seemingly arbitrary line of code, which doesn't relate to the piece of code with allocation and deallocation. I'm a little confused as to why this would be. If I move the allocation/deallocation it segfaults on the deallocation line: deallocate(labelArray). Any idea why?Windbound
@Pureferret Post code sample please? Segfault on deallocate line probably means you are trying to deallocate some array/index which has not been allocated.Romonaromonda
I've spotted what's going wrong; yes I haven't allocated labelArray before deallocating. Will go fix. Edit: It works now. Thanks everyone!Windbound
M
11

If you declare an array allocatable, you use deffered shape in the form real,

allocatable :: labelArray(:,:)

, or

real,dimension(:,:),allocatable :: labelArray

with number of double colons meaning rank (number of your indexes) of your array.

If the array is unallocated you use

 allocate(labelarray(shapeyouwant))

with the correct number of indexes. For example allocate(labelarray(2:3,-1:5)) for array with indexes 2 to 3 in demension 1 and -1 to 5 in dimension 2.

For change of dimension you have to deallocate the array first using

deallocate(labelArray)

To reallocate an allocated array to a new shape you first need to allocate a new array with the new shape, copy the existing array to the new array and move the reference of the old array to the new array using move_alloc().

  call allocate(tmp(size_old+n_enlarge))
  tmp(1:size_old) = array(1:size_old)
  call move_alloc(tmp, array)

The old array is deallocated automatically when the new array reference is moved by move_alloc().


Fortran 95 deallocates arrays automatically, if they fall out of scope (end of their subroutine for example).

Fortran 2008 has a nice feature of automatic allocation on assignment. If you say array1=array2 and array1 is not allocated, it is automatically allocated to have the correct shape.

It can also be used for re-allocation (see also Fortran array automatically growing when adding a value and How to add new element to dynamical array in Fortran 90)

 labelArray = [labelArray, new_element]
Maccarone answered 5/12, 2011 at 11:46 Comment(3)
So is this a valid statement? allocate(labelArray(size(labelArray)+1)) All I want is to increase the size when I add on an element.Windbound
No, it is not possible. You can not change allocated dimension of the array. It would be to complicated. The program would have to make a copy anyway quite possibly. You have to 1)make a new bigger array and copy to it, or 2) make a copy of your array, deallocate it, allocate it bigger and copy the saved values back. If your compiler supports it, you can use move_alloc, instead of copy, then you don't have to deallocate your array, just allocate it bigger and copy the saved values back.Coper
Actually, too complicated... there is realloc in C, but Fortran just doesn't have it.Coper
P
1

Late comment... check Numerical Recipes for Fortran 90. They implemented a nice reallocate function that was Fortran 90 compliant. Your arrays must be pointer attributed in this case, not allocatable attributed.

The function receives the old array and desired size, and returns a pointer to the new resized array.

If at all possible, use Fortran 95 or 2003. If 2003 is impossible, then 95 is a good compromise. It provides better pointer syntax.

Permission answered 11/6, 2014 at 13:45 Comment(0)
C
0

Summarizing the above answers, the state of art method of increasing size of an allocatable array in Fortran is as follows:

  allocate(tmp(size_old+n_enlarge))
  tmp(1:size_old) = array(1:size_old) !copy the data
  call move_alloc(tmp, array) !rename

Note that move_alloc only involves copying the internal descriptors (not the acutal data in array), so that the storage pointed to is the same.

In summary, move_alloc provides an efficient way to reallocate an array to a larger size without copying the data twice (you need to copy the data only once). That is, you need a copying in, but you do not need a copying out.

Caffey answered 27/4, 2023 at 11:41 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.