Last updated: 28 November 2006
Met Office
FitzRoy Road, Exeter
Devon, EX1 3PB
United Kingdom
© Crown copyright 2006. All rights reserved.
Questions regarding this document or permissions to quote from it should be directed to the IPR Manager.
Main contents:
Fortran is the standard programming language at the Met Office for developing scientific and research applications, in particular, for programs running on the supercomputers. This document describes some guidelines that should be followed by developers when writing Fortran code, especially for code in systems hosted by FCM.
To get the most out of the FCM build system, you should follow these guidelines when you develop your code:
A clash of program unit names happens most often when you have multiple versions of the same program unit in the same source tree. You should design your code to avoid this situation. If it is not possible to do so, you may have to use a pre-processor to ensure that there is only one copy of each program unit in the source tree. Another situation where clashes of program unit names may occur is when you are developing code that is shared between several projects. In this case, you may want to agree with the other projects a naming convention to define a unique namespace for program units in each project. (E.g. some projects at the Met Office have adopted a naming convention such that all shared program units in a project are named with a unique prefix.)
Internal sub-program | |
---|---|
Place sub-programs in the CONTAINS section of a
standalone program unit. There are two advantages for this approach.
Firstly, the sub-programs will get an automatic interface when the
container program unit is compiled. Secondly, it should be easier for
the compiler to provide optimisation when the sub-programs are
internal to the caller. The disadvantage of this approach is that the
sub-programs are local to the caller, and so they cannot be called by
other program units. Therefore, this approach is only suitable for
small sub-programs local to a particular program unit.
Note: One way to share a sub-program unit between several top level program units is to make use of the Fortran INCLUDE statement. You can write the sub-program unit in a separate file and place it in the CONTAINS section of different program units using INCLUDE statements. The disadvantage of this approach is that when the program is compiled, a copy of the sub-program unit will be embedded within each of the top level program units. This may lead to an increase in size of the executable, and so this approach is still only suitable for small sub-programs local to a small number of program units. Example: |
|
In the file "sub_prog.inc":
SUBROUTINE sub_prog (some, arg) ! Some declarations ... ! Some executable statements ... END SUBROUTINE sub_prog |
In the file "bar.f90":
SUBROUTINE bar (more, arg) ! Some declarations ... ! Some executable statements ... CALL sub_prog (some, arg) ! More executable statements ... CONTAINS INCLUDE 'sub_prog.inc' END SUBROUTINE bar |
Module procedures | |
---|---|
Place sub-programs in the CONTAINS section of a
MODULE. Again, the sub-programs will have automatic interfaces when
the MODULE is compiled. If you give the sub-programs the PUBLIC
attribute (which is the default), you will be able to call them from
anywhere using the current MODULE. You will also gain all the
advantages offered by a MODULE. (E.g. a MODULE will allow you to
design your code in a more object-oriented manner.) However, MODULE
dependency can have an impact on the efficiency of incremental
compilations. For example, if you modify items that are local to the
MODULE, it is very difficult for the build system to detect that your
change does not affect program units using the MODULE, so the build
system will end up compiling the MODULE and all the program units that
use it.
Example: |
|
In the file "my_mod.f90":
MODULE my_mod ! Some module declarations CONTAINS SUBROUTINE sub_prog (some, arg) ! Some declarations ... ! Some executable statements ... END SUBROUTINE sub_prog END MODULE my_mod |
In the file "foo.f90":
SUBROUTINE foo (some, arg) USE my_mod, ONLY: sub_prog ! Some declarations ... ! Some executable statements ... CALL sub_prog (some, arg) ! More executable statements ... END SUBROUTINE foo |
Interface files | |
---|---|
For each source file containing a standalone
SUBROUTINE or FUNCTION, FCM generates a file containing the interface
of the SUBROUTINE or FUNCTION. By default, the generated file is named
after the original source file, but with the file extension replaced
by "*.interface". In the specification section of the caller routine,
you will then be able to declare the interface using a Fortran INCLUDE
statement to include the interface file. This type of INCLUDE
statement is detected automatically by FCM, which will use it to set
up the dependency tree.
The advantage of using an interface file is that the caller is now dependent on the interface file, rather than the SUBROUTINE or FUNCTION itself. If you change the SUBROUTINE or FUNCTION without modifying its interface, the build system will not re-compile the caller in incremental build, (but it will be intelligent enough to re-link the executable with the updated object). Note: By default, an interface file is named after the original source file. Bearing this in mind, it is worth noting that file names in a Unix/Linux system are case-sensitive, and so the interface file name declared by your INCLUDE statement is also case sensitive. If you use an incorrect case in the INCLUDE statement, the dependency tree will be set up incorrectly and the compilation will fail. Another problem is that if you do not name your file after the program unit, the dependency tree will be wrong. To avoid this problem, it is recommended that all source files are named in lower case after the program units they contain. (Alternatively, you can use the TOOL::INTERFACE option in the FCM build configuration file to allow you to alter the default behaviour so that the interface file is named after the "program" unit in lowercase. We may alter FCM in the future so that this will become the default. In the mean time, it is highly recommended that you use this option and design your new code accordingly.) Example: |
|
In the file "sub_prog.f90":
SUBROUTINE sub_prog (some, arg) ! Some declarations ... ! Some executable statements ... END SUBROUTINE sub_prog |
In the file "egg.f90":
SUBROUTINE egg (some, arg) ! Some declarations ... INCLUDE 'sub_prog.interface' ! More declarations ... ! Some executable statements ... CALL sub_prog (some, arg) ! More executable statements ... END SUBROUTINE egg |
Interfaces in a module | |
---|---|
There is also a half-way house approach between the
second and the third options. You can have a dedicated MODULE where a
large number of INCLUDE interface file statements are placed. Other
program units get their interfaces by importing from this MODULE. A
major disadvantage of this approach is that the sub-programs with
their interfaces declared within this MODULE will not be able to call
any other sub-programs declared within the same MODULE, as it will run
into a cyclic dependency problem. Another disadvantage is that if an
interface changes, the MODULE and all program units depending on the
MODULE will have to be re-compiled, even though the change may be
unrelated to some or all of these program units. For these reasons,
this approach is only good if you have a bundle of sub-programs that
have relatively stable interfaces and are very much independent of one
another.
Note: a similar approach can be useful when you have a library of legacy or external code. In this situation, you will simply declare the interfaces for all the library sub-programs in the MODULE. Any programs that call sub-programs within the library can then import their interfaces by using the MODULE. Example: |
|
In the file "my_i_mod.f90":
MODULE my_i_mod ! Some declarations INCLUDE 'sub_prog.interface' ! More declarations END MODULE my_i_mod |
In the file "ham.f90":
SUBROUTINE ham (some, arguments) USE my_i_mod, ONLY: sub_prog ! Some declarations ... ! Some executable statements ... CALL sub_prog (some, arguments) ! More executable statements ... END SUBROUTINE ham |
FCM also supports the use of a "! DEPENDS ON" directive for users to specify a dependency from within a source file. This feature is documented in the Further dependency features sub-section of the FCM user guide. However, it is worth noting that this method is only included in FCM to support legacy code. It is not a feature recommended for new code, and its use should be gradually phased out from existing code.
Common practice | Better approach |
---|---|
SUBROUTINE foo (a, b, c) INTEGER :: a, b, c, i, j, k ! ... END SUBROUTINE foo |
SUBROUTINE foo (a, b, c) INTEGER :: a, b, c INTEGER :: i, j, k ! ... END SUBROUTINE foo |
We do not recommend the use of C pre-processor with Fortran. However, it is acknowledged that there are some situations when it is necessary to pre-process Fortran code. FCM supports pre-processing in two ways. Pre-processing can be left to the compiler or it can be done in a separate early stage of the build process. A separate pre-process stage can be useful if pre-processing changes any of the following in a program unit:
However, using a separate pre-process stage is not the best way of working, as it adds an overhead to the build process. If your code requires pre-processing, you should try to design it to avoid changes in the above.
In practice, the only reasonable use of C pre-processor with Fortran is for code selection. For example, pre-processing is useful for isolating machine specific libraries or instructions, where it may be appropriate to use inline alternatives for small sections of code. Another example is when multiple versions of the same procedure exist in the source tree and you need to use the pre-processor to select the correct version for your build.
Avoid using the C pre-processor for code inclusion, as you should be able to do the same via the Fortran INCLUDE statement. You should also avoid embedding pre-processor macros within the continuations of a Fortran statement, as it can make your code very confusing.
The guidelines in this section are recommended practices for programming Fortran in general. These are guidelines you should try to adhere to when you are developing new code. If you are modifying existing code, you should adhere to its existing standard and style where possible. If you want to change its standard and style, you should seek prior agreements with the owner and the usual developers of the code. Where possible, you should try to maintain the same layout and style within a source file, and preferably, within all the source code in a particular project.
When reading these guidelines, it is assumed that you already have a good understanding of modern Fortran terminology. It is understood that these guidelines may not cover every aspect of your work. In such cases, you will be a winner if you use a bit of common sense, and always bearing in mind that some other people may have to maintain the code in the future.
Always test your code before releasing it. Do not ignore compiler warnings, as they may point you to potential problems.
The following is a list of recommended practices for layout and formatting when you write code in Fortran.
Common practice | Better approach |
---|---|
DO i=1,n a(i)%c=10*i/n b(i)%d=20+i ENDDO IF(this==that)THEN distance=0 time=0 ENDIF |
DO i = 1, n a(i) % c = 10 * i / n b(i) % d = 20 + i END DO IF (this == that) THEN distance = 0 time = 0 END IF |
Common practice | Better approach |
---|---|
REAL, INTENT (OUT) :: my_out (:) REAL, INTENT (INOUT) :: my_inout (:) REAL, INTENT (IN) :: my_in (:) ! ... CHARACTER (LEN = 256) :: my_char my_char = 'This is a very very' // & ' very very very long' // & ' character assignment'. |
REAL, INTENT ( OUT) :: my_out (:) REAL, INTENT (INOUT) :: my_inout (:) REAL, INTENT (IN ) :: my_in (:) ! ... CHARACTER (LEN = 256) :: my_char my_char = 'This is a very very' // & ' very very very long' // & ' character assignment'. |
The following is a list of recommended styles when you write code in Fortran.
Common practice | Better approach |
---|---|
IF (my_var == some_value) THEN something = .TRUE. something_else = .FALSE. ELSE something = .FALSE. something_else = .TRUE. END IF IF (something .EQV. .TRUE.) THEN CALL do_something () ! ... END IF |
something = (my_var == some_value) something_else = (my_var /= some_value) IF (something) THEN CALL do_something () ! ... END IF |
Common practice | Better approach |
---|---|
IF (my_var != some_value) THEN CALL do_this () ELSE CALL do_that () END IF |
IF (my_var == some_value) THEN CALL do_that () ELSE CALL do_this () END IF |
else if | end do | end forall | end function |
end if | end interface | end module | end program |
end select | end subroutine | end type | end where |
select case | - | - | - |
Common practice | Better approach |
---|---|
INTEGER :: array1(10, 20), array2(10, 20) INTEGER :: scalar array1 = 1 array2 = array1 * scalar |
INTEGER :: array1(10, 20), array2(10, 20) INTEGER :: scalar array1(:, :) = 1 array2(:, :) = array1(:, :) * scalar |
Common practice | Better approach |
---|---|
a = b * i + c / n |
a = (b * i) + (c / n) |
The following is a list of Fortran features that you should use or avoid.
Common practice | Better approach |
---|---|
ALLOCATE (a(n)) ALLOCATE (b(n)) ALLOCATE (c(n)) ! ... do something ... DEALLOCATE (a) DEALLOCATE (b) DEALLOCATE (c) |
ALLOCATE (a(n)) ALLOCATE (b(n)) ALLOCATE (c(n)) ! ... do something ... DEALLOCATE (c) DEALLOCATE (b) DEALLOCATE (a) |
Common practice | Better approach |
---|---|
INTEGER, DIMENSION(10) :: array1 INTEGER :: array2 DIMENSION :: array2(20) |
INTEGER :: array1(10), array2(20) |
The following is a basic template for a SUBROUTINE:
SUBROUTINE <subroutine_name> (<arguments>, ...) ! Description: ! <Explain the usage of the subroutine and what it does.> ! ! (c) Crown copyright Met Office. All rights reserved. ! For further details please refer to the file COPYRIGHT.txt ! which you should have received as part of this distribution. ! ------------------------------------------------------------------------------ ! Modules <module declarations, each with a list of imported symbols> IMPLICIT NONE ! Arguments: <arguments with INTENT ( OUT)> <arguments with INTENT (INOUT)> <arguments with INTENT (IN )> ! Local declarations: <parameters, derived data types, variables, etc> ! INTERFACE blocks <INCLUDE interface blocks for external procedures> <interface blocks for procedure and operator overloading> !------------------------------------------------------------------------------- <... subroutine executable statements> !------------------------------------------------------------------------------- CONTAINS <sub-programs> END SUBROUTINE <subroutine_name>
Note:
For example:
!------------------------------------------------------------------------------! ! ! ! (C) Crown copyright 2005-6 Met Office. All rights reserved. ! ! ! ! Use, duplication or disclosure of this code is subject to the restrictions ! ! as set forth in the contract. If no contract has been raised with this copy ! ! of the code, the use, duplication or disclosure of it is strictly ! ! prohibited. Permission to do so must first be obtained in writing from the ! ! Head of Numerical Modelling at the following address: ! ! ! ! Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB, United Kingdom ! ! ! !------------------------------------------------------------------------------!