-
Notifications
You must be signed in to change notification settings - Fork 25
Code Formatting Rules (STRAWMAN)
We want the code in MARBL to look uniform across modules and we want developers to be able to easily trace calls throughout the code. This page provides a list of rules developers must follow - we will provide a tool to ensure no rules are broken (or at least to check the "easy to check" rules), and that tool will be run on all pull requests. Pull requests will not be considered for inclusion in the main code base until they pass the following checks.
Note: these rules are based on comments made in issues #87 and #139. Also, this is written with the Fortran code in mind, but we will also come up with similar requirements for python code.
-
White space and indentation
- No hard tabs
- Indentation should be 2 spaces
- Indent contents of
associate
blocks - Continuation lines should be indented 5 spaces from the beginning of the call
- No trailing white space
- Spaces on both sides of operators (
c = a + b
, notc=a+b
). EXCEPTION: no space around=
for named arguments in function call:call foo(bar=4)
- Spaces after all commas (multiple arguments to functions,
do
loops, etc) - Spaces in all
end
lines:end module
,end if
, etc - Spaces in
else *
statements:else if
andelse where
-
Even though Fortran is case-insensitive, be sure to use the same case for all references to variables, subroutines, etc
-
Fortran intrinsics should be in all lowercase:
implicit none
,sqrt(...)
,null()
, etc -
Every module should have a module-level
implicit none
statement, so there should not be any at the subroutine-level -
Lines should not exceed 120 characters, not including continuation character
- Continuation characters should be one space after the final character of the line (not necessarily in column 122)
- This includes comments!
-
Visual dividers:
- Between subroutine definitions, divider should be
!
followed by 77*
(with two-space indenting, the last*
should be in column 80):
end subroutine foo !***************************************************************************** subroutine bar()
- There should be a similar divider between the last subroutine and the
end module
statement
end subroutine foo !***************************************************************************** end module bar
- Dividers inside functions and subroutines should use the same definition:
!
in the correct column given the current indentation, and the last*
in column 80.
- Between subroutine definitions, divider should be
-
Use
'
instead of"
around all strings
-
Variable declarations may have multiple variables in a single line, but should never use a continuation character
Allowed:
integer :: var1, var2, var3 integer :: var4, var5, var6
Not allowed:
integer :: var1, var2, var3, & var4, var5, var6
-
Do not use the
dimension
attribute for declaring arraysAllowed:
integer :: array(5)
Not allowed:
integer, dimension(5) :: array
-
Aligning variable declarations
-
vertically align text for similar attributes (but not for commas)
integer, allocatable, intent(in) :: vector1(:) real(r8), allocatable, intent(out) :: array1(:,:)
-
-
Do not explicitly declare array lengths for subroutine arguments
Allowed:
integer, intent(in) :: array(:)
Not allowed:
integer, intent(in) :: array(5)
-
Alwaya specify a
kind
number for real variables, but omit thekind=
prefix:real(r8) :: real_var
-
Floating point numeric constants need kind specifier:
1.0_r8
, not just1.0
-
Strings should be declared with the explicit
len=
prefix:character(len=char_len) :: string_var
-
-
Variables and subroutines should be named using
snake_case
, notCamelCase
-
Associate statements should only contain one variable per line, and none on the first or last line (unless there is just one variable association)
associate(& var1 => variable1, & var2 => variable2, & var3 => variable3 & )
-
Explicitly show array dimensions in associate statement
associate(& var1 => variable1, & vec1 => vector1(:), & arr1 => array1(:,:,k) & )
-
Arguments should be organized such that all ``intent(in)``s come first, followed by ``intent(inout)``s and finally ``intent(out)``s
- Obviously optional arguments come last, but they should also be organized in this manner
- Exception:
marbl_status_log
is alwaysintent(inout)
but should be the last argument (not counting optional arguments)
-
Subroutine definitions and subroutine calls should use the same formatting for line breaks in the argument list:
subroutine some_subroutine(var1, var2, & var3, var4)
should be called with
call some_subroutine(var1, var2, & var3, var4)
- When feasible, a subroutine with array arguments should also take the array length as an
intent(in)
. Declaring arguments to be dimension(:)
is allowed as a last resort but discouraged.
-
use
statements may have multiple variables in a single line, but should never use a continuation characterAllowed:
use some_module, only : var1, var2, var3 use some_module, only : var4, var5, var6
Not allowed:
use some_module, only : var1, var2, var3, & var4, var5, var6
-
use
statements should only appear at the module level if they are needed by module variables (e.g. kind numbers, derived types), otherwise they should be used in at the subroutine-level.Note that this may mean many subroutines in a module use the same variable from another module. That seems like a reasonable trade-off for the ease with which we can move subroutines between modules