Extra Additions/Opinions/Corrections are very Welcome
AcknowledgementsKudos to Scott Belmonte and Dave Love for providing the examples and information on this. Some mistakes and false interpretations may have occurred in the translation of this information. Thus all blame for errors or dodgy observations should be passed to the author of this page, CCP14 Secretary, Lachlan Cranswick (E-mail: l.cranswick@dl.ac.uk). |
Background and WarningThe strict Fortran 77 specification does not allow Automatic Arrays (Dynamic Arrays/Dynamic Memory Allocation). However, the freely available GNU Fortran 77 compiler (G77) does allow you to use the Fortran 90 Automatic Array methods with a very good chance that it will compile happily and run when compiled with G77. Other commercial Fortran 90 compilers should not have a problem with being able to use Automatic Arrays as this is allowed under the Fortran 90 specification. However, 100% strict Fortran 77 compilers will not be able to compile code that uses dynamic memory allocation. GNU G77 can handle Automatic Arrays due to it having some Fortran 90 extensions. An example of this are giving below. Other options could to use a combination of Fortran and C code for managing Automatic Arrays. An example of this is given below. Be wary that this could be a very compiler and OS specific method and tweaks may be required when porting to a different system. Refer to: Array processing (part of the Resources for Learning Fortran 90)
Also refer to Alternative Opinions on which Linux and Fortran compiler and multi booting system to use for Crystallography And refer below to: and
|
Manually Increasing arrays and Ulimit Problems on UNIX MachinesOne method for getting around small static array sizes in Fortran programs is just to increase the relevant array sizes and re-compile. However, you may find that programs do not run or fail midstream with cryptic or zero indication why this is occuring. If unfamiliar with this property of UNIX, it may take quite some time to realise what is going on. An example of this type of behaviour follows. ./maps: /sbin/loader: Fatal Error: cannot map Main The problem here is the program is exceeding the amount of memory an individual program is allowed to request under a UNIX shell. The default allowed amount of memory can vary depending on the shell and the version of UNIX. A quick fix could be to change to a different shell (such as bash). A possible better idea would be to do man ulimit and find out what options your operating system allows for this. |
Examples of F90 style Dynamic Arrays/Automatic Arrays in Fortran that will compile on GNU F77/G77
Example from Scott Belmonte
Date: Tue, 23 May 2000 10:48:55 +0100 (BST) From: "Scott A. Belmonte" To: "L. Cranswick" [L.M.D.Cranswick@dl.ac.uk] Subject: Re: What does it mean when a Fortran program does this? On Mon, 22 May 2000, L. Cranswick wrote: > > Hi Scott, > > have been increasing the arrays of a fortran > program to get it to handle a larger problem on > a Digital Alpha machine - but it gives an error like: > > 22879:maps: /sbin/loader: Fatal Error: cannot map Main > pxsv6% ./maps > 18152:./maps: /sbin/loader: Fatal Error: cannot map Main > > > What does this means - arrays going over each-other or > something like this? > I've not come across this exact problem before, and I would need to see the code and the actual compilation instructions, but judging by what you are doing to the code, it sounds like the arrays you are defining are to big to be allocated on the stack. You can do two things to get round this: 1) The stack usage is normally limited by the shell. Type ulimit -a to see the restrictions. If you are running csh/tcsh then there is not much you can do about is because for some reason it does not let you increase the limit. You could try running bash, it lets you change the limits. Change the stack limit using ulimit -s #, where # is the number of 512-byte blocks to set the limit to, or ulimit -s unlimited to grab as much as you're allowed. If this does not work then go to 2. 2) Dynamically allocate the memory at run time. This actually is always the best thing to do when data arrays start to become very big and has the advantage that you don't need to know the size of the array before you run the program. There is no standard way to dynamically allocate objects in f77. You will have to use the libc malloc() function. You can do dynamic allocation in Fortran 90. I'm not very hot in Fortran 90 (I'm a C man myself) but something like this should work: PROGRAM alloc_example C Allocate a 1-d real array with N elements INTEGER N, ALLOC_ERR REAL, ALLOCATABLE :: DATA(:) C You would calculate N at run time or see it to the valus you want. N = 10 C ALLOC_ERR is set to a positive integer (i.e. not zero) if an error C occur with the allocation, such as out-of-memory. ALLOCATE (DATA(N), STAT = ALLOC_ERR) IF (ALLOC_ERR.NE.0) THEN WRITE(*,*) 'ERROR: Could not alloc memory' STOP END IF C Zero array, or do what ever you want to do with it. DO I = 1,N DATA(I) = 0.0 END DO C Once you finished, deallocate the memory. DEALLOCATE(DATA) END Digital Alpha has a Fortran 90 compiler (f90) but it might be a nightmare trying to compile old f77 code with it. There is another possiblity that the linker, for some reason, cannot find the main block. This is entirely due to the compilation. But seeing that it has compiled and worked before (I assume) then this is unlikely to be the problem. I hope this helps. I can help you with incorporating malloc into the code but linking with c routines is quite non-portable so once you have changed the code to work with Alpha compilers you may ahve to customise for any other machine that you want to compile on. Scott. --------------------------------------------------------------------- Dr Scott A. Belmonte University of Edinburgh Room C18A Daresbury Laboratory, Daresbury Warrington WA4 4AD ---------------------------------------------------------------------
Example from Dave Love
Date: Thu, 25 May 2000 16:42:49 +0100 To: l.cranswick@dl.ac.uk Subject: dynamic allocation From: Dave Love User-Agent: Gnus/5.0807 (Gnus v5.8.7) Emacs/21.0.90 Here's a trivial example of f90 automatic arrays. $ cat alloc.f subroutine allocs (n) real vector (n) do i=1,n vector (i) = i end do print *, vector end print *, 'Array dimension?' read *, n call allocs (n) end $ g77 alloc.f && ./a.out Array dimension? 10 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. $ ----------------------- Here's the C part of the support I wrote for CCP4: /* \section{Dynamic memory allocation} */ /* It's nice to be able to determine array sizes at run time to avoid */ /* messy recompilation. The only way effectively to get dynamic */ /* allocation in Fortran77 reasonably portably is to do the allocation, */ /* e.g.\ in C, and invoke the Fortran routine passed as a parameter with */ /* pointers to the allocated memory which it will treat as arrays. If we */ /* want to allow more than one array, it's more tricky. */ /* */ /* \subsection{{\tt subroutine ccpal1 (\meta{routne}, \meta{n}. */ /* \meta{type}, \meta{length})}} */ /* Arranges to call subroutine \meta{routne} with \meta{n} array */ /* arguments. Each has a type indicated by \meta{type}$(i)$ and a length */ /* given by \meta{length}($i$). \meta{type} is an integer array with */ /* values 1, 2, 3, 4 inidcating {\tt */ /* INTEGER}, {\tt REAL}, {\tt DOUBLE PRECISION} and {\tt COMPLEX} */ /* respectively. */ /* It's not immediately clear what all the Fortran/C */ /* conventions are for passing [[CHARACTER]] arrays, so we'll arrange a */ /* higher-level interface and have [[types]] here just numeric. The */ /* Fortran ([[CCPALC]]) will also do argument validation. Also the rules */ /* for passing external routines as arguments aren't clear---assume */ /* the obvious way. */ /* */ /* There's a \idx{VMS} Fortran version of this, although the code here */ /* does work fine in VMS\@. */ /* */ /* NB: there's a possibility of a hook here to use memory-mapped files on */ /* systems with the capability and insufficient VM\@. */ /* */ /* Under protest, this now allocates zeroed storage for where programs */ /* make bad assumptions. */ /* */ /* <miscellaneous routines>= */ #ifndef VMS /* we'll use the Fortran version in VMS*/ #ifndef _MVS #if CALL_LIKE_HPUX void ccpal1 (routne, n, type, length) void (* routne) (); int *n, type[], length[]; #endif #if defined (VMS) || CALL_LIKE_STARDENT void CCPAL1 (void (* routne) (), int *n, int type[], int length[]) #endif #if CALL_LIKE_SUN void ccpal1_ (void (* routne) (), int *n, int type[], int length[]) #endif #if CALL_LIKE_MVS void __stdcall CCPAL1 (void (* routne) (), int *n, int type[], int length[]) #endif { int i, size, *leng[13]; void *pointer[13]; for (i=0; i<*n; i++) { switch (type[i]) { case 1: size = item_sizes[6]; break; /* integer */ case 2: size = item_sizes[2]; break; /* real */ case 3: size = 2*item_sizes[2]; break; /* double */ case 4: size = 2*item_sizes[2]; break; /* complex */ case 5: size = item_sizes[1]; break; /* bytes (logical or integer *1) */ } pointer[i+1] = calloc ((size_t) length[i], (size_t) size); if (pointer[i+1] == NULL) fatal ("CCPALC: can't allocate memory"); leng[i+1] = &(length[i]); /* convenience */ } switch (*n) { case 1: (* routne) (leng[1], pointer[1]); break; case 2: (* routne) (leng[1], pointer[1], leng[2], pointer[2]); break; case 3: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3]); break; case 4: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4]); break; case 5: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5]); break; case 6: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6]); break; case 7: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7]); break; case 8: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7], leng[8], pointer[8]); break; case 9: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7], leng[8], pointer[8], leng[9], pointer[9]); break; case 10: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7], leng[8], pointer[8], leng[9], pointer[9], leng[10], pointer[10]); break; case 11: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7], leng[8], pointer[8], leng[9], pointer[9], leng[10], pointer[10], leng[11], pointer[11]); break; case 12: (* routne) (leng[1], pointer[1], leng[2], pointer[2], leng[3], pointer[3], leng[4], pointer[4], leng[5], pointer[5], leng[6], pointer[6], leng[7], pointer[7], leng[8], pointer[8], leng[9], pointer[9], leng[10], pointer[10], leng[11], pointer[11], leng[12], pointer[12]); break; } for (i=0; i<*n; i++) free (pointer[i+1]); } ----------------------- And the Fortran: SUBROUTINE CCPALC(ROUTNE, N, TYPE, LENGTH) C ========================================== C C Arrange to call subroutine ROUTNE with N array arguments each of C length LENGTH (i) and type indicated by TYPE (i): 'i' == integer, C 'r' == real, 'd' == double precision, 'c' == complex, 'b' == C "byte" (logical*1 or integer*1, unportable and deprecated) . TYPE C elements may have either case. C Consider `call ccpalc (fred, 3, types, lens)' with types = (/'i', C 'r', 'c'/) and lens = (/1000, 2000, 3000/). This effectively does C call fred (1000, arr1, 2000, arr2, 3000, arr3) C with C subroutine fred (n1, foo, n2, bar, n3, baz) C integer n1, n2, n3, foo (n1) C real bar (n2) C complex baz (n3) C ... C Obviously all communication with ROUTNE must be by COMMON (or, C possibly, extra ENTRYs). The allocated memory is freed on return C from ROUTNE. As a concession, it's initially filled with zeroed C bytes. C C Arguments: C ========== C C ROUTNE (I) EXTERNAL: routine to call C N (I) INTEGER: number of arguments to ROUTNE (<=12) C TYPE (I) CHARACTER*1 (*): type of arguments to ROUTNE: C 'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION; C 'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1 C LENGTH (I) INTEGER*(*): number of elements in each (array) C argument of ROUTNE C_END_CCPALC C C .. Scalar Arguments .. INTEGER N C .. C .. Array Arguments .. CHARACTER TYPE (*) INTEGER LENGTH (*) C .. EXTERNAL ROUTNE, CCPAL1, CCPUPC INTEGER I, ITYPE (12) CHARACTER TTYPE (12) C .. IF (N.LT.1 .OR. N.GT.12) + CALL CCPERR (1, 'CCPALC: bad number of arguments') DO 10 I=1,N TTYPE (I) = TYPE (I) CALL CCPUPC (TTYPE (I)) ITYPE (I) = INDEX ('IRDCB', TTYPE (I)) IF (ITYPE (I) .EQ. 0) CALL CCPERR (1, 'CCPALC: bad TYPE: '// + TYPE (I)) IF (LENGTH (I).LE.0) CALL CCPERR (1, 'CCPALC: length <=0') 10 CONTINUE CALL CCPAL1 (ROUTNE, N, ITYPE, LENGTH) END C C C_BEGIN_CCPALE SUBROUTINE CCPALE(ROUTNE, N, TYPE, LENGTH, LENDEF, PRINT) C ================================================= C C Arrange to call subroutine ROUTNE with N array arguments each of C length LENGTH (i) and type indicated by TYPE (i): 'i' == integer, C 'r' == real, 'd' == double precision, 'c' == complex, 'b' == byte. C TYPE elements may have either case. LENGTH points to an array of C environment variable (logical) names from which integer values are C read. The lengths default to values from LENDEF. C This is a convenient interface to CCPALC to allow configuring of C the memory requirements on the command line where appropriate. C This may be useful if the memory requirements can't be determined C initially and it's necessary to guess. C C Arguments: C ========== C C ROUTNE (I) EXTERNAL: routine to call C N (I) INTEGER: number of arguments to ROUTNE (<=12) C TYPE (I) CHARACTER*1 (*): type of arguments to ROUTNE: C 'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION; C 'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1 C LENGTH (I) CHARACTER *(*): logical names representing the number C of elements in each (array) argument of ROUTNE C LENDEF (I) INTEGER (*): default lengths for the argument arrays C used if the appropriate LENGTH argument doesn't represent a C defined logical C PRINT (I) LOGICAL: whether or not to print the values of the C array lengths C_END_CCPALE C C .. Scalar Arguments .. INTEGER N LOGICAL PRINT C .. C .. Array Arguments .. CHARACTER TYPE (*), LENGTH (*)*(*) INTEGER LENDEF (*) C .. EXTERNAL ROUTNE, CCPE2I, CCPALC, LUNSTO INTEGER I, LENG (12), CCPE2I, LUNSTO C .. DO 10 I=1,N LENG (I) = CCPE2I (LENGTH (I), LENDEF (I)) 10 CONTINUE IF (PRINT) THEN WRITE (LUNSTO(1), + '(/'' Memory allocation (logical name, type, elements):'')') WRITE (LUNSTO(1), '(3X, A, 1X, A, 3X, I10)') + (LENGTH (I), TYPE (I), LENG (I), I=1,N) ENDIF CALL CCPALC (ROUTNE, N, TYPE, LENG) END |
|
|
|
|
|
|
PGPLOT Graphics Subroutine Library - Tim Pearson
|
DISLIN Scientific Data Plotting Software - Helmut Michels
|
Numerical Recipes in Fortran (and a book on Fortran 90) - the Art of Scientific Computing
|
Alternatives to Numerical Recipes.
|
Resources for Learning Fortran 90/Fortran-90 Resources |
Fortran Market
|
ftnchek - GPL static Fortran 77 analyzer
|
Numerical methods for Fortran programmers
|
C-Fortran Interface |
Mixing Fortran and C |
Calling C from Fortran |
Interfacing Fortran and C
|
CFORTRAN: Interfacing C and FORTRAN |