This is an old revision of the document!
Table of Contents
Coding Conventions
Stick to the standard
- Code enabled by default should be standard Fortran2003 [-std=f2003]
- Avoid using new OOP aspects, because compilers do not yet support them well enough.
- OpenMP code should follow the version 3.X of the standard
- MPI should should follow the version 2.X of the standard
Write explicit code
- Every
USE
-statement should have anONLY:
-clause, which lists the imported symbols [-Wuse-without-only]. - Every
OMP PARALLEL
-region should declaredefault(none)
. - Every static variable should be marked with the
SAVE
attribute. - Every Fortran module should contain the line
IMPLICIT NONE
[-fimplicit-none]. - Every conversion that might change value should be explicit [-Wconversion]. Rationale:
- INTEGER i=4.9999, i.e. i=INT(4.9999) implies i==4. Use NINT(4.9999) as appropriate.
- natom*natom, nrow_global*ncol_global overflows INT(KIND=4) for large simulations.
- the global number of grid points (pw_grid_type%ngpts,ngpts_cut) overflows INT(KIND=4) for large simulations.
Don't use poorly designed language features
- Do not use left-hand-side (lhs) reallocations of allocatables [-Wrealloc-lhs-all]. Why?
- Do not use
FORALL
constructs. Why? - Do not use
OMP THREADPRIVATE
variables. - Do not query the
STAT
from aDEALLOCATE
, the Fortran runtime takes care. - Do not use
RANDOM_NUMBER()
, it's not consistent across different compilers.
Fight spaghetti code
There are two measure of defense against spaghetti code:
- Decoupling on the module and package level:
- Every module should depend on as few other modules as possible.
- Every package should depend on as few other packages as possible.
- Information hiding, also known as encapsulation.
- External libraries should be wrapped within a single module or package.
- Every module should hide its content by containing the line
PRIVATE
and only few public symbols. - Every package should hide its content by providing only a small public API through a single module.
Use existing infrastructure
For many common operation there exist wrappers in CP2K to prevent usage errors and to allow for central redirections.
- Use the routines from
cp_files.F
instead of callingOPEN
andCLOSE
directly. - Use the routines from the full-matrix
fm
-package instead of calling BLAS or Scalapack directly. - Use the routines from
message_passing.F
instead of calling MPI directly. - Use the routines from
machine.F
to access architecture depended things like e.g. the working directory. - Don't use
UNIT=*
inWRITE
orPRINT
statements. Instead request a unit from the logger:iw=cp_logger_get_default_unit_nr()
and write only if you actually received a unit:IF(iw>0) WRITE (UNIT=iw, ,,,)
. - Use CP2K's error-handling, instead of the
STOP
statement.
Remove dead code
- Every line of code has to be compiled and maintained. Hence, unused variables and code poses an unnecessary burden and should be removed [-Wunused-variable -Wunused-but-set-variable -Wunused-dummy-argument].
- Sometimes it is beneficial to keep debugging code around nevertheless. Such code should be put into a
IF(debug_this_module)
-block, with a parameter set to.FALSE.
. This way the code will stay up-to-date and easily callable.
Format and document code
- Each files should start with the official CP2K header.
- Each
.F
file should contain either aPROGRAM
or a singleMODULE
, whose name matches the filename. - Each module and routine should be annotated with Doxygen documentation.
- Each preprocessor flag should start with two underscores and be documented in the
INSTALL
-file and added to the cp2k_flags function (cp2k_info.F). - The code should be formatted with the prettify-tool by running
make -j pretty
.
Write tests
- Every feature should be tested, with the goal of complete code coverage by regtesting .
Doxygen documentation
- Every
FUNCTION
andSUBROUTINE
should be preceded by a valid doxygen block. - The following keywords are required:
\brief
,\param
(for each parameter),\retval
(for functions) - The following keywords are optional:
\note
,\par
,\date
,\author
- Please run
make doxify
to format your doxygen comments, or generate templates where none exist - See our doxygen pages for the result
Formatting conventions and auto-formatting (prettify)
Uniform formatting of CP2K source code will be enabled in the near future by a prettify script that is an almost complete auto-formatter for Fortran 90 source code. As a rule of thumb, developers should not worry about the format of their code and just let prettify do its magic by running make -j pretty
. The chosen formatting conventions for CP2K are as follows:
- Sorting and alignment of variable declarations and
USE
statements, removal of unused list entries. - Indentation with a relative width of 3 characters.
- Line continuations are aligned with the previous opening delimiter
(
,[
or(/
or with an assignment operator=
or=>
. If none of the above is present, a default hanging indent of 3 characters is applied. - All operators are surrounded by exactly one whitespace character, except for arithmetic operators.
- Removal of extraneous whitespace and consecutive blank lines.
- Uppercase notation for all Fortran and OpenMP keywords.
The following formatting decisions are still manual and are never changed by prettify:
- Positions of line breaks (except for variable declarations and
USE
statements). - No indentation of subsequent
DO
/IF
statements that are aligned with each other.
There may be cases where manual alignment is preferred over the rules stated above. The following options for manual formatting are provided:
- No automatic realignment of line continuations that are prefixed with an
&
. - No auto-formatting of lines to which a comment starting with
!&
is attached. - No auto-formatting of code blocks enclosed between two comment lines starting with
!&<
and!&>
.
A few examples to illustrate how to deal with cases where auto-formatting produces unsatisfying results:
- Reduce hanging indent by inserting linebreaks directly after assignment operator and opening delimiter:
! No: long_result_var_name = long_function_name(arg_1, arg_2, & arg_3, arg_4, arg_5)+ & foo ! Yes: long_result_var_name = & long_function_name( & arg_1, arg_2, & arg_3, arg_4, arg_5)+ & foo
- Avoid linebreaks in deeply nested expressions:
! No: bessj0 = (r1+y*(r2+y*(r3+y*(r4+y* & (r5+y*r6)))))/(s1+y*(s2+y*(s3+y* & (s4+y*(s5+y*s6))))) ! Yes: bessj0 = (r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6)))))/ & (s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))
- Alignment by explicit bracketing:
! No: foo = bar+foobar(x1, y1, z1)* & foobar(x2, y2, z2)* & foobar(x3, y3, z3) ! Yes: foo = bar+(foobar(x1, y1, z1)* & foobar(x2, y2, z2)* & foobar(x3, y3, z3))
- Special vertical alignment may require manual formatting:
! Auto-formatting: align_me = [-1, 10, 0, & 0, 1000, 0, & 0, -1, 1] ! Manual alignment (!& disables whitespace formatting): align_me = [-1, 10, 0, & !& 0, 1000, 0, & !& 0, -1, 1] !& ! Alternatively: !&< align_me = [-1, 10, 0, & 0, 1000, 0, & 0, -1, 1] !&>