This page outlines the coding conventions adhered to in the CLASSIC source code. Some rules are marked as (required), and should be followed at all times unless it is breaking to the code. Other rules are (suggested), meaning it is a good idea to follow them, but failing to do so will not necessarily be grounds for rejecting the code from integration.
Language and Units
- Program components and comments should be written in simple English unless there exists a very good reason for not doing so (required)
- Program components include variables, functions, subroutines, modules, filenames, and other constructs
- Always bear in mind that your code may be read by people who are not proficient English speakers
- Variables, subroutines, functions, and modules should have names which clearly represent their intended functions (required)
- Single-letter variables are acceptable only for transient use where the code is easily readable
- Units must be clearly stated in comments (required)
- SI units are prefered unless there is a good reason not to use them
- Standard SI prefixes may be used
Case conventions
- Case conventions vary by project, and contributors are encouraged to follow whatever case conventions are used. CLASSIC adopts camelCase for most of its subroutine and variable naming, so contributors are expected to do the same (required)
Free-Form Fortran
- Free-form Fortran 90 / 95 is the standard for this codebase; source code files should have a suffix of .f90 or .F90 (required)
- F90 is more readable, easier to maintain, and boasts native support for more functionality
- Fixed-form syntax contains many obsolete features and is more difficult to read
General Structure
- Modules should always have their own file (required)
- Related functions, subroutines, and variables should be grouped together into modules with descriptive names (suggested)
- Modules, functions, and subroutines should always end with its own name to help delineate the extent of that construct within the source code (required)
module foo
...
end module foo
subroutine bar( )
...
end subroutine bar
integer function moo( )
...
end function moo
Whitespace
- Two-space indentation blocks will be used for this codebase (required)
- Tabs are not to be used in place of spaces
- Block comments should follow the same whitespace guideline as the code block they are contained within (required)
- Trailing whitespace should be eliminated from every line (required)
do i = 1, 50
if (MODULO(i, 5) == 0) then
call someRoutine(i)
! this is a comment block
! using the same whitespace guideline as
! the code block in which it is contained
end if
end do
Alignment
- Effort should be made to align the attributes of code segments if readability will be improved (suggested)
- Only do this if it won’t add excessive whitespace
real, intent(in) :: inputArgument
real, intent(inout) :: inputOutputArgument
real, intent(out) :: outputArgument
rmlmossac_t => ctem_tile%rmlmossac_t
gppmossac_t => ctem_tile%gppmossac_t
litrmsmossrow => vrot%litrmsmoss
Cmossmasrow => vrot%Cmossmas
dayl_maxrow => vrot%dayl_max
sdeprot => class_rot%sdeprot
RADJROW => class_rot%RADJROW
Line Length and Continuation
- Maximum line length should be 120 characters including indentation (suggested)
- This increases readability of code, and applies to comments as well
- Continuation lines will have an ampersand at the end of the line to be continued (required)
- A space should be present before the ampersand, and this should all be contained within the suggested 120 character limit
- The following line will be indented by additional whitespace at the discretion of the developer to maintain a clean look (see below)
character(50) :: person_one, person_two, person_three, &
person_four
person_one = "Ada Lovelace"
person_three = "Alan Turing"
Variable Declaration and Attributes
Variable Description
- Variables should be described using either inline comments for each variable, or with a comment block describing a group of variables (suggested)
- Comments should be Doxygen readable, with LaTeX use where appropriate to allow for proper Doxygen rendering (required)
real, intent(out) :: RHOSROT(NL, NM) !< Density of snow \f$[kg m^{-3} ]\f$
real, intent(out) :: SNOROT (NL, NM) !< Mass of snow pack \f$[kg m^{-2} ]\f$
Implicit None
implicit none
must be declared in every program component (required)- This prevents implicit variable declarations, a huge source of bugs
Attributes
-
Variable declarations must use a double colon
::
even if no other attributes are present (required)- Bad:
integer example_variable
- Good:
integer :: example_variable
- Bad:
-
If multiple declarations occur with the same attributes, the attributes should be listed in the same order (required)
integer, allocatable, pointer :: foo
integer, allocatable, pointer, dimension(:,:) :: bar
Subroutine Variables
- Variables used within a subroutine should have the
intent()
attribute matching their intended use (required) - Whenever possible, arguments passed into a subroutine should have names matching those found within the subroutine (suggested)
- This applies to function calls as well
subroutine square_cube (i, i_squared, i_cubed)
implicit none
integer, intent(in) :: i ! input
integer, intent(out) :: i_squared, i_cubed ! output
i_squared = i ** 2
i_cubed = i ** 3
end subroutine square_cube
program xx
implicit none
integer :: i, i_squared, i_cubed
i = 4
call square_cube (i, i_squared, i_cubed)
print *, "i, i^2, i^3 = ", i, i_squared, i_cubed
end program xx
Fortran Modules
- Avoid using module level variables (suggested)
- Always specify
use, only
when importing from another module (required)- This helps developers trace where variables, functions, and subroutines originate and makes the organization of the code much clearer and helps reduce clutter in the namespace.
module foo
use bar, only : fun1, fun2
...
end module foo
- Entities within a module should be set to private by default, and declared public only when they are intended to be accessed from outside the module
module exampleModule
implicit none
...
private
! Sets default to private
...
integer, parameter :: alpha, beta, delta, gamma
...
public :: alpha, beta
! Sets the specified entities to public
contains
...
end module exampleModule
Operators
Array arithmetic
- Explicit indexing will be used for array arithmetic (required)
- This prevents undefined behaviour caused by unintentional operations on array halos
Example (where A, B, C are 2-dimensional arrays)
Bad:
C = A*B
Good:
C(i,j:i,k) = A(i,j:i,k) * B(i,j:i,k)
Assignment and Arithmetic Operators
- Assignment and arithmetic operators should maintain a space on either side (suggested)
- This improves interpretability of arithmetic operations and assignments
x = ((15 - y) / (13 * z)) ** 3
Relational and Logical Operators
- The new standard relational operators will be used, as shown below (suggested)
- These apply to comparable data types such as
real
orcharacter
, and prevent accidental mixing with logical types
- These apply to comparable data types such as
Old (deprecated) syntax | New syntax |
---|---|
.eq. | == |
.ne. | /= |
.lt. | < |
.le. | <= |
.gt. | > |
.ge. | >= |
- Logical variables and operations will continue to appear similar to the old relational syntax
.true. .false.
variables and.eqv. .neqv. .not. .and. .or.
operators
- Logical evaluations should not use redundant operations as shown below: (suggested)
Bad:
doProc = .true.
...
if (doProc .eqv. .true.) then
...
endif
if (doProc .neqv. .true.) then
...
endif
Good:
doProc = .true.
...
if (doProc) then
...
endif
if (.not. doProc) then
...
endif
Control Flow
Loops
- Loops will terminate with
end do
and notcontinue ____
(required)- Each loop must terminate with its own
end do
(no sharing loop terminators) - This is an F90 standard to promote comprehension
- Each loop must terminate with its own
- Loops will not be labelled with numbers as was common in F77 (required)
do i = 1, 10
! logic happens in here
...
end do
- In the case of nested or overlapping logic constructs, descriptive labelling may be used to distinguish these constructs (suggested)
outer_loop: do i = 1, 10
...
inner_loop: do j = 1, 20
...
end do inner_loop
...
end do outer_loop
If-Else Statements
- Positive logic should be used in if-evaluations when the contained code blocks are of comparable length (suggested)
- Positive logic refers to checking that a variable is true rather than false
- An example is given below:
if (exampleVariable /= 5) then
call routineX()
else
call routineY()
end if
Note the negative logic expression in the if test. This is easily refactored:
if (exampleVariable == 5) then
call routineY()
else
call routineX()
end if
- Include a space after the
if
and before starting the logical expression (suggested) - Group compound logical expressions using parentheses (required)
if ( (a == b) .and. (b == c) ) then
end if
Goto
- Goto statements are to be avoided whenever possible (required)
- If absolutely necessary, goto statements should be thoroughly documented (required)
- Including the reason for their use
Commenting and Documentation
- Liberal use of good comments is encouraged
- See Language and Units above for language guidelines
- Inline comments should be descriptive and concise
- Longer documentation should be placed at the end of the file
- Changelogs are not necessary due to the use of a Version Control System (VCS) (required)
- VCSs like git tracks users and changes automatically
- Doxygen is the documentation tool used in this repository. Comments should conform to Doxygen’s Fortran standards as outlined here
The subroutine example from above has been commented using Doxygen’s specifications here:
!> Computes the square and cube of an input value, and
!! writes them to the output values
!! @param i input value
!! @todo Shame users for passing in negative numbers
subroutine square_cube (i, i_square, i_cube)
implicit none
integer, intent(in) :: i !< Our input variable
integer, intent(out) :: i_square, i_cube !< Our output variables
i_square = i ** 2
i_cube = i ** 3
end subroutine square_cube
Code templates
Acknowledgements
Several coding conventions were used as a guide for developing this document. Joint UK Land Environment Simulator (JULES) Coding Standards NASA Goddard ModelE Coding Conventions GEM-MACH Coding Standards