Skip to content

Code Formatting Rules (STRAWMAN)

Michael Levy edited this page Jun 12, 2018 · 7 revisions

Coding Standards

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.

General Formatting

  1. 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, not c=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 and else where
  2. Even though Fortran is case-insensitive, be sure to use the same case for all references to variables, subroutines, etc

  3. Fortran intrinsics should be in all lowercase: implicit none, sqrt(...), null(), etc

  4. Every module should have a module-level implicit none statement, so there should not be any at the subroutine-level

  5. 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!
  6. 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.
  7. Use ' instead of " around all strings

Variable Declarations

  1. 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
  2. Do not use the dimension attribute for declaring arrays

    Allowed:

    integer :: array(5)

    Not allowed:

    integer, dimension(5) :: array
  3. Aligning variable declarations

    • vertically align text for similar attributes (but not for commas)

       integer,  allocatable, intent(in)  :: vector1(:)
       real(r8), allocatable, intent(out) :: array1(:,:)
  4. Do not explicitly declare array lengths for subroutine arguments

    Allowed:

    integer, intent(in) :: array(:)

    Not allowed:

    integer, intent(in) :: array(5)
  5. Alwaya specify a kind number for real variables, but omit the kind= prefix:

    real(r8) :: real_var
    • Floating point numeric constants need kind specifier: 1.0_r8, not just 1.0

    • Strings should be declared with the explicit len= prefix:

      character(len=char_len) :: string_var
  6. Variables and subroutines should be named using snake_case, not CamelCase

Associate statements

  1. 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 &
         )
  2. Explicitly show array dimensions in associate statement

    associate(&
         var1 => variable1, &
         vec1 => vector1(:), &
         arr1 => array1(:,:,k) &
         )

Subroutines

  1. 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 always intent(inout) but should be the last argument (not counting optional arguments)
  2. 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)
  1. 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

  1. use statements may have multiple variables in a single line, but should never use a continuation character

    Allowed:

    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
  2. 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

Logical comparisons

  1. Use symbols instead of .[word]. operators for numerical comparisons: `` >= `` rather than `` .ge. ``, `` == `` rather than `` .eq. ``, etc.