Skip to content
Robert Hallberg edited this page May 11, 2018 · 28 revisions

MOM6 code style guide

Code style is typically a personal choice. When styles clash

White space

  • No tabs
  • No trailing white space
  • Indents should be consistent (same amount used in contiguous blocks)
  • Preferred indent is 2 spaces
    • "preferred" might understate the reaction invoked by other indent amounts! 😉
  • No white space between a function name and the opening parenthesis
  • White space after all language token
    • if(a==0) is legal fortran but bad style. Use if (a==0) instead.
    • if (a == 0) is even better, since == is a language token.
  • Use space around the equal sign in variable assignment, but not when using a named optional argument
    • a = b is strongly preferred over a=b
      • One exception is loop indices, where do i=is,ie is acceptable
    • call fn(arg1, arg_name=a) is strongly preferred over call fn(arg1, arg_name = a)

Line length

Some compilers handle very long lines of code gracefully, but MOM6 needs to adhere to the Fortran standard, which is 132 characters for code, after any macro expansion. MOM6 does use macros for some memory declarations, so we need to build in some added space in setting MOM6 guidelines:

  • The target maximum length for MOM6 code lines (exclusive of comments) is 100 characters
    • 80 character lines can be much easier to read if printed; smaller lines are encouraged where they make sense
  • No lines of MOM6 code should exceed 120 characters, including comments

Local variables

  • Local variable declarations appear after all the dummy argument declarations, and in the case of a function the return value. We often use ! Local variables to delineate between the argument and local variable declarations.
  • Local variables should preferably be descriptive multi-character names meaningful in their context, e.g. del_rho_int (delta rho at interface).
  • If using a highly abbreviated or short name, the declaration MUST be commented.
  • Multi-word names should use snake_case (e.g. delta_rho).
    • snake_case admittedly used more characters than camelCase but unfortunately doxygen interprets the Fortran standard too literally and throws away any attempts to use CamelCase. We briefly adopted CamelCase for new code but are systematically replacing it as we doxygen-ize existing code.

Loop indices

Soft case convention

  • i,j,k are used for cell-center, layer-center references, e.g. h(i,j,k), T(i+1,j,k).
  • I,J are used for staggered, cell-edge references, e.g. u(I,j,k), v(i,J,k), q(I,J,k), u(I-1,j,k). We use a north-east staggering convention so the I means i+1/2 and I-1 means i-1/2.
  • K is used for the interface (between layer) references, e.g. del_t(i,j,K) = T(i,j,K+1) - T(i,j,K). The vertical staggering is such that interface K=1 is above layer k=1 so that K means k-1/2 and K+1 means k+1/2.

Global / module data

  • Absolutely NO!
  • There are a few exceptions which are strictly for debugging non-shared memory applications. Do not use these as an excuse for adding module data.

Module use statements

  • Modules may use interfaces, data-types, and constant parameters from other modules via module use statements
    • Modules may not use variables from other modules via use statements
    • All MOM variables are passed around as explicit arguments in interfaces.
  • All module use statements must include the , only modifier

Implicit variables

  • Absolutely NO!
  • All MOM6 modules must declare implicit none ; private
    • Top-level drivers (i.e., files declaring a program main()) only need implicit none

Array syntax

  • We do not permit scalar-style expressions without the colon notation, e.g.
    • tv%S = 0. is forbidden.
  • We do allow array syntax for whole array initialization, e.g.
    • tv%S(:,:,:) = 0.
  • We do not allow whole array-syntax for math expressions that include halos because halos are not guaranteed to have valid data:
    • tmp(:,:) = 1.0 / G%areaT(:,:) might have zeros in the halo region.
    • call post_data(id_AT, G%areaT(:,:)*tv%(T(:,:,1)) is wrong because it can use uninitialized data in halos.

Data flow

  • All needed data is passed via arguments to subroutines and functions, or as the returned value of a function.
  • All arguments must have declared intent, with the exception of pointers: i.e. intent(in), intent(out), intent(inout).
  • Opaque types are preferred, i.e. referencing members of types defined in other modules is discouraged.

Documentation in code

  • Do it when you are writing the code in the first place!
  • All subroutines, functions, arguments, and elements of public types should be described in with dOxygen comments.
  • All real variables should have a full physical description, including units.
  • All comments should be clearly written and grammatically correct; American spelling is preferred.

Optimization for divides

Divisions are prone to NaNs and relatively expensive. An optimizing compiler will often rearrange math which makes debugging divisions by zero harder to catch.

  • Many common reciprocals are pre-computed
    • Use Q(i,j) * G%IareaT(i,j) instead of Q(i,j) / G%areaT(i,j).
  • Never write B / C * D which is ambiguous to humans (not the compiler)
    • Use ( B * D ) / C
  • Never double divide: A / ( A + B / C)
    • Use ( A * C ) / ( A * C + B)
Clone this wiki locally