Coding standards
The standards in this document are largely prescriptive, i.e., they should be followed.
- MUST: Exceptions must be discussed and agreed to by the CAM SEs and noted in this document.
- SHOULD: Exceptions must be approved by the CAM SEs and documented in the ChangeLog.
While some legacy code will not follow these rules, efforts SHOULD be made to improve the code whenever you are working on it (e.g., bug fixes, enhancements).
General coding standards
MUST
- Always use spaces instead of tabs
- No trailing spaces (i.e., no spaces at the end of a line)
See tips for configuring editors
SHOULD
- Use comments to explain the purpose of the following code and/or include any important but non-obvious information. When working with code, always check the comments to make sure they are still correct and useful.
- Do not use comments to 'save code for later in case it might be useful'.
- Do not include a comment that merely restates the following code logic (e.g., 'Loop over variables')
Python coding standards
We expect all python code to pass with a perfect score (10) using pylint
with this version of pylintrc
. However, external repos may have their own pylintrc
version depending on their needs.
We also expect all python files to follow the black code style and format.
Finally, one can also follow the Google Python Style Guide.
Fortran coding standards
The standards described in this section represent the CAM Fortran standards. Other Fortran standards:
MUST
- No naked
use
statements - No continued single-line
if
statements (i.e., allif
statements should have athen
if the statement is on more than one line) - Every namelist variable in each active namelist group is present in the namelist file. An active namelist group is one which may be read during the current run.
- All namelist variables except for logical quantities are initialized to invalid values (integer:
-HUGE(1)
, real:NaN
, character:'UNSET'
). - Functions may not have side effects, and should include the
pure
keyword. - Do not combine statements on a single line (i.e., avoid use of the semi-colon to combine statements).
- Use
intent
for dummy arguments except for pointers. - All variables of type real must have a specified kind, including literals. For example, use
1.5_r8
, not1.5
or1.5D0
. Literals must also include the decimal point. - All character declarations must use Fortran 90+ syntax (e.g.,
character(len=*)
orcharacter(len=CL)
). - All variable declarations must use Fortran 90+ syntax (i.e., must include the double colon between the attributes and the variable name).
- All type and procedure declarations must use Fortran 90+ syntax (i.e., must include the double colon before the type or procedure name).
- All modules should include an
implicit none
statement in the preamble (after theuse
statements). Module routines then do not need this statement. - All optional arguments must be passed via keyword (e.g. use
call subroutine(x, optional_y=y)
instead ofcall subroutine(x, y)
for the optional variableoptional_y
). - Initialize local (non-parameter) variables in subroutines and functions at the top of the executable code, NOT on a variable declaration lines.
- Initializing a local variable on a declaration line invokes the
SAVE
attribute and is not thread safe. - Local pointer variables MUST be initialized before other (non-initialization) statements. By default, use the
nullify
statement.
- Initializing a local variable on a declaration line invokes the
- All variables that are on the physics grid must have their horizontal dimension declared with
pcols
, even if only a subset of the variable is used in the subroutine or function.
SHOULD
- Avoid use of preprocessor directives (e.g.,
#if
,#ifdef
). Always try for runtime variable logic instead. - Keep formula statements relatively short. Use temporary variables to break long formulas into easier-to-read sections.
- Use subroutines to avoid repeated (cut and paste) code logic.
- Avoid side effects in subroutines. Pass variables to routines instead of 'using' them from elsewhere.
- Use the
pure
keyword if a subroutine has no side effects. - List dummy arguments one per line, however, related items may be grouped.
- Dummy argument order should match the order in the argument list.
- Use symbolic numerical comparison operators (e.g.,
==
,/=
,<
,>=
) not old character versions (e.g.,.eq.
). - Avoid the use of pointers as dummy arguments (exceptions must be discussed in design or code review)
- Modules should be default
private
. Public interfaces are declared after theprivate
declaration. private
module interfaces (i.e., subroutines and functions) should be declared private in the module header.- Module names should conform to their filename (i.e., the module name should be the filename without the
.F90
). - Functions should use the
pure
attribute. If they cannot, the reason should be included as a comment in the function's preamble. - All functions and subroutines should avoid un-necessary statements (e.g. a blank
return
at the end of a subroutine). use
statements should be brought in at the smallest scope possible (e.g. inside individual subroutines instead of at the module level).
Indentation and style
- Scoping: Indentation should follow scope. That is, whenever entering a new scope (e.g.,
module
,subroutine
,if
,do
), indent that scope relative to the scoping statement (recommended 3 spaces but each module should at least be self consistent). - A single line should be less than 133 characters long.
- Continue lines: Indent continue lines 5 spaces or align with similar lines in statement.
- Use spaces to ease reading statements (e.g., before and after operators, after commas except in a dimensions list)
- Include a space after
if
,else
,end
,do
, andwhile
. - Include a space before and after
::
- No space after
only
, i.e.,only:
, notonly :
. - When aligning code for readability, commas go immediately after a symbol (no space).
Tips for configuring editors
emacs (add to your .emacs
file)
- To automatically remove trailing spaces whenever you save a file:
(add-hook 'before-save-hook 'delete-trailing-whitespace)
- To automatically indent with spaces instead of tabs:
(setq-default indent-tabs-mode nil)
- To use 4 spaces for each indent:
(setq tab-width 4)
vi (add to your .vimrc
file)
- To automatically remove trailing spaces whenever you save a file:
autocmd BufWritePre * :%s/\s\+$//e