HowTo: LMDZ code guidelines
/!\ This page is a WORK IN PROGRESS. Nothing in this page should be considered correct or consensual for now. /!\
Note: this documents draws from various sources, including the OPA guidelines.
Sommaire
Example code
Note: in this example, we added "meta-comments" indicated by !!
Note: of course, there's always exceptions to "Always", but they should systematically documented by a comment, with a proper explanation.
1 !! File: lmdz_mymodule.f90
2 !! ^ all module files should start with their component name, (e.g. "lmdz")
3 !! ^ use ".f90" for all free-form files, unless they contain preprocessor directives (then use ".F90").
4
5 !! At the top of the file, we provide a comment to explain what this modules does
6 ! implementation of my_useful_stuff.
7 ! ----------------------------------
8
9 !! Unless specified otherwise, all files should contain a single module with the same name which encapsulates all their content
10 MODULE lmdz_mymodule
11 !! ^ all Fortran keywords are in CAPS, everything else is in lowercase.
12 USE my_other_module, ONLY: function_a, function_b
13 !! ^ Always specify explicitely which functions you use from a module.
14 !!^ use indentation (2 spaces) to provide visual clarity
15 IMPLICIT NONE; PRIVATE
16 !! ^ Always make your module private, and expose explicitely which functions are public
17 !! ^ Always use IMPLICIT NONE at the module-level
18 PUBLIC my_var, my_subroutine
19
20 REAL, PARAMETER :: my_var
21 REAL :: a, b, c, d, e, f
22 !! ^ use "::" even if it's not strictly necessary
23 !! ^ Alignment of "::" is not necessary, but should be attempted whenever it provides readability and is relevant (e.g. there's some link between the aligned variables)
24
25 CONTAINS
26
27 !! Put at least a blank line before/after each function/subroutine
28 SUBROUTINE my_subroutine(arg1, arg2, arg3) ! handle event xxxx
29 !! ^ comments should always be right before, or on the same line as what they're commenting
30 !! ^ as for modules, all functions should be documented in a comment
31 INTEGER, INTENT(IN) :: arg1
32 !! ^ Always provide intent
33 REAL, INTENT(IN) :: arg2
34
35 INTEGER, INTENT(INOUT) :: arg3
36
37 REAl, INTENT(OUT) :: out1
38 !!^ Group together IN, then INOUT, then OUT
39
40 IF (arg2 > arg1) THEN
41 !! ^ .ge., .le., etc are deprecated. Use >, <, ==, etc instead
42 ! ...
43 END IF
44 !! ^ Use END IF, END DO rather than ENDIF, ENDDO (coherence with fortran-lang.org)
45 END SUBROUTINE my_subroutine
46 !! ^ always use named blocks for END
47
48
49 END MODULE my_module
50 !! ^ always use named blocks for END
Some comments:
- Limit to the bare minimum the use of preprocessor
#ifdef
keys. Those decrease lisibility, disable automatic code analysis, and generally make code a pain to manage and read. Ideally, a given preprocessor key should be used only once, or if not possible, in a single module for the entire codebase. - Limit the use of
include
headers to the bare minimum. Whenever not possible, wrap those include in a module, and import that module instead. This provides much more flexibility, e.g. when usingONLY: ...
.
Misc
Fortran standard
Fortran 2008 is now widely supported on all major compilers, and should be taken as a reference.
Allocatable arrays
Allocatable arrays can be relevant, but should be substituted by automatic arrays (= passing the array size as an argument) whenever possible, for performance and debugging reasons.
Compiler warnings
Compiler warnings are useful - unless there's too many of them. Effort should be made such that during the compiling phase, no warning appears, so that user can be easily alerted of potential bugs. when some appear in their configuration.
In particular, all component must be able to run when a compile-time and/or run-time array bounds checking option is enabled. Use of the (*) construct in array dimensioning to circumvent this problem is forbidden because it effectively disables array bounds checking.
Side effects
Whenever possible, write functions without side-effects, and label them accordingly as PURE
or ELEMENTAL
.
SAVE
Since all files contain a MODULE
, attribute SAVE
is banned. Variables whose value have to be preserved between two calls should be declared at the module level.
Deprecated features
In terms of keeping code up to date and easier to maintain the code should always follow the current standards of FORTRAN and ANSI C. We decide to restrict the languages use to the elements, which are not obsolete or deleted -- even if they are still available with almost all compilers.
Note: just because a feature is deprecated doesn't mean it must be converted in old, working code - this can be tricky and bug-prone in complex instances.
Some notable deprecated features:
-
COMMON
blocks. Instead, useMODULE
initialisation. -
EQUIVALENCE
should be replaced by pointers or derived data types. - Any kind of
GOTO
has absolutely no place in modern programming. UseCASE
whenever relevant rather thanIF
. - Arithmetic
IF
statements. Use blockIF
orCASE
instead. -
CONTINUE
statement: useIF, CASE, DO WHILE, EXIT, CYCLE
statements or a containedSUBROUTINE
instead. - I/O routines
END
andERR
- useIOSTAT
instead -
FORMAT
statements: use character parameters or explicit format- specifiers inside theREAD
orWRITE
statement instead. -
ENTRY
statements: a subprogram must only have one entry point - Alternate
RETURN
is obsolescent, andRETURN
at the end of a function is useless clutter. - Statement functions have been replaced by corresponding functions or subroutines in the
CONTAIN
block of the current scope. -
DATA
andBLOCK DATA
should be replaced by initializers.
Questions à élucider
- Prescrit-on une langue (Français / Anglais) ?
- Longueur des lignes: dans la limite du raisonable, pourquoi se limiter à 100-140 chars ? Le wrapping fonctionne très bien.