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.