MCTDH: Dynamic allocation

Back to Main Menu

The Dynaccess Library

Using Dynamic Allocation in MCTDH

Allocation

Use variables of type

integer pointer(2)

to store pointers (can be shared in common blocks).

call alloc(pointer, n)

where n is the number of bytes to allocate to allocate a block of memory. The pointer is stored in pointer.

Use the functions complexsize(), realsize(), integersize() and logicalsize() from lib/utilities/platform.F as prefactors to create portable code, e.g. n=realsize()*len to allocate a double precision array that can hold len numbers.

Resizing

call resize(pointer, m)

to resize the array to the new size in bytes m, data already present in the array is copied into the new location.

If pointer is set to zero prior to calling realloc, this routine can also be used instead of alloc. This is useful when an array is to grow in a loop along with the arrival of data.

Writing to Arrays

Data is stored in an array via

call set****(value, pointer, index)

where **** stands for character, short, long, lli, float, double, scpx, dcpx.

These can be used to access elements of arrays that store integer*1 (bytes), integer*2 (words), integer*4, integer*8, real*4, real*8 (double precision), complex*8 (single complex), and complex*16 (double complex) variables.

The set**** call can be envisaged as corresponding to the Fortran statement

array(index) = value

Reading from Arrays

For most types, the elements in a dynamic array are accessed via

variable = get****(pointer, index)

where **** are as before and the statement is equivalent to Fortran's

variable = array(index)
.

Note that the getlli and the get?cpx calls for long long int and complex numbers cannot be called this way.

Instead, they need to be called procedurally, i.e. the above Fortran call corresponds to

call getdcpx(variable, pointer, index)

This has become necessary due to a shortcoming in the specification of the Fortran standard which allowed different Fortran compilers to implement passing of complex function results differently.

All other functions get**** can be used just like any ordinary Fortran function, e.g. in a debug statement of the form

write(0,'(e16.8)')(getdouble(buffer,i),i=1,buflen)

Note also that with setlli you can actually store pointer variables, so you can create arrays of arrays (i.e. two-dimensional arrays). This was done with arrays of arrays of double precision for in source/opfuncs/linint.F. Have a look to see how it was done.

Freeing Memory

One of the advantages of using dynamic memory allocation is that it allows to free memory, so that more memory is available for other sections of the program.

Memory allocated can be deallocated by calling either of

call dealloc(pointer)
call resize(pointer, 0)

Block Access

Random access to data is almost as fast with the dynaccess library as on Fortran 77 arrays. However, sequential data access is much faster with Fortran 77 arrays.

In order to achieve better performance for sequential access of large blocks of data, blockwise operations on dynamical arrays can be performed by

call setblock(buffer, pointer, offset,length)
call getblock(buffer, pointer, offset,length)  

where buffer(*) is a Fortran array of any type that is used to store data to or read it from the dynamic array, integer pointer(2) is a Fortran pointer placeholder which has been initialised with alloc or resize, offset is the offset in bytes from the start of the memory designated by pointer and length is the length in bytes of the data block to copy.

Use the functions complexsize(), realsize(), integersize() and logicalsize() to translate array indices and lengths into byte offsets and lengths.

Note that copying from the beginning of the memory block is done by using offset=0, i.e. we are counting from zero instead of counting from one, which would have been the usual Fortran way (and the way it is done in all other get**** and set**** routines to make them less confusing to use from Fortran).

This convention is more natural when operating with complexsize(), realsize() etc. and hence has been used for the ***block subroutines.

The speed advantage of the ***block subroutines stems from the fact that large data sets are transferred in one function call.

Dynamic Allocation of Arrays Containing Character Strings

An array containing strings is an array of arrays containing characters, which is a common problem in programming, so special routines were devised for this case. Allocation:

call newstring(pointer, n)

where n is the number of stings to be stored.

Resizing is done via

call resizestring(pointer, n, o)

where n is the new allocation size and o is the old one. Freeing all memory occupied by the array of string pointers and the strings themselves is done with either of

call freestring(pointer)
call releasestring(pointer, n)  

Array elements (i.e. strings) are set with

call setstring(string, pointer, index)

which corrsponds to

array(index) = string

and read out with

call getstring(string, pointer, index)

which corresponds to

string = array(index)

Note that the latter is not a function but a procedure because we are copying an array of characters into the character*(*) variable string.

This routine is fail-safe, i.e. if the string variable is shorter than what was stored in the array element, only the first few characters are retrieved; if it is longer, it is filled up with white blanks (as is the Fortran way).

Note that setstring allocates enough memory to hold the string string, i.e. calling it via

call setstring(string(start:end), pointer,index)

will save memory if you only need a part of a string.

These techniques were used in source/opfuncs/linint.F. Have a look to see how it was done.