Syntax rules in QE
Dear QE developers,
After carefully thinking about a general syntax rules for the code, trying to make them as consistent with each other as possible, I propose the following rules:
Pre-processing
- Preprocessing options should be capitalized and start with two underscores. Examples:
__MPI, __LINUX, ... - Use preprocessing syntax
#if defined (XXX), not#if defined XXXor#ifdef XXX
Common style
- Fortran commands should be capitalized:
CALL something(XXX) - Variable names should be lowercase:
foo = bar/2 - Use
(KIND = DP)(defined in module kinds) to define the type of real and complex variables
Order of declaration
- The recommanded order is as follow:
CHARACTER(LEN = 256) :: var
LOGICAL :: var
LOGICAL, ALLOCATED :: var(:)
INTEGER :: var
INTEGER, ALLOCATED :: var(:)
REAL(KIND = DP) :: var
REAL(KIND = DP), ALLOCATED :: var(:)
COMPLEX(KIND = DP) :: var
COMPLEX(KIND = DP), ALLOCATED :: var(:)- First all
INTENTvariables are declared (in that order) and then all the local variables are declared (in that order). - Note: Do not use
DIMENSION(:)
Typical header of subroutines
!------------------------------------------------------------------------
SUBROUTINE name(arg1, arg2)
!------------------------------------------------------------------------
!!
!! Description of subroutine
!!
!------------------------------------------------------------------------
USE kinds, ONLY : DP
USE cell_base, ONLY : at, bg, alat
!
IMPLICIT NONE
!
! Input variables
INTEGER, INTENT(in) :: arg1
!! Description
REAL(KIND = DP), INTENT(in) :: arg2(3, 5)
!! Description
!
! Local variables
INTEGER :: ik
!! Description
!------------------------------------------------------------------------
END SUBROUTINE name
!------------------------------------------------------------------------Indentation
- Use two spaces for indentation
DO ik = 1, nkf
DO imode = 1, nmodes
code
ENDDO
ENDDO Spaces
- Leave one space after a comma "," and between "multiple conditions" in a IF statement
IF (cond) THEN
CALL name(arg1, arg2, arg3)
ENDIF
ALLOCATE(var1(dim1, dim2), STAT = ierr)
IF (ierr /= 0) CALL io_error('Error allocating var1 in subroutine_name')
DO ik = 1, nkf
ikk = 2 * ik - 1
ikq = 2 * ik
IF ((MINVAL(ABS(var1(:, ikk) - ef)) < fsthick) .AND. (MINVAL(ABS(var1(:, ikq) - ef)) < fsthick)) THEN
ENDDO
DEALLOCATE(var1, STAT = ierr)
IF (ierr /= 0) CALL io_error('Error deallocating var1 in subroutine_name')Allocating and deallocating arrays
- Check the status once an array is allocated or deallocated
ALLOCATE(var1(dim1, dim2), STAT = ierr)
IF (ierr /= 0) CALL errore('subroutine_name', 'Error allocating var1', 1)
DEALLOCATE(var1, STAT = ierr)
IF (ierr /= 0) CALL errore('subroutine_name', 'Error deallocating var1', 1)Reading and writing files
- Leave one space after a comma "," and after a statement
OPEN(UNIT = file_unit, FILE = 'file_name', STATUS = 'old', FORMAT = 'formatted', IOSTAT = ios)
IF (ios /= 0) CALL errore('subroutine', 'error opening file_name', iunit_name)
READ(file_unit) index
CLOSE(file_unit)
OPEN(UNIT = file_unit, FILE = 'file_name', STATUS = 'old', FORMAT = 'formatted', IOSTAT = ios)
IF (ios /= 0) CALL errore('subroutine', 'error opening file_name', iunit_name)
WRITE(file_unit, '(i7)') index
CLOSE(file_unit)Intrinsic functions
- Use capital letters when calling an intrinsic function or logical:
a = MATMUL(c, d)
c = TRANSPOSE(DBLE(e))
f = .TRUE.Relational operator
- Use modern relational operators:
> instead of .gt.
< instead of .lt.
== instead of .eq.
/= instead of .neq.Mathematical operator
- Use one space between mathematical operators
a = b + i
c = c / SQRT(s)Spaces in the code
- Avoid white space in the code. When a space is needed, add a comment (!) that follows the indentation:
!
a = b
!
DO i = 1, n
!
y = a + c
ENDDOConditional allocation
- Do NOT use:
IF (.NOT. ALLOCATED(var)) ALLOCATE(var(dim1)) - Do use:
ALLOCATE(var1(dim1, dim2), STAT = ierr)
IF (ierr /= 0) CALL errore('subroutine_name', 'Error allocating var1', 1)Indeed conditional allocations create potential memory leaks and can always be avoided.
Good practice
- Conversions should be explicitly indicated. For conversions to real, use
DBLE, or elseREAL(..., KIND = DP). For conversions to complex, useCMPLX(...,...,KIND = DP). For complex conjugate, useCONJG. For imaginary part, useAIMAG. - Do not use
REALorCMPLXwithoutKIND = DP, or else you will lose precision (except when you take the real part of a double precision complex number). - Do not use automatic arrays (e.g.
REAL(KIND = DP) :: A(N)with N defined at run time) unless you are sure that the array is small in all cases: large arrays may easily exceed the stack size, or the memory size. - Do not use pointers unless you have a good reason to: pointers may hinder optimization. Allocatable arrays should be used instead.
- If you use pointers, nullify them before performing tests on their status.
- Be careful with F90 array syntax and in particular with array sections. Passing an array section to a routine may look elegant but it may turn out to be inefficient: a copy will be silently done if the section is not contiguous in memory (or if the compiler decides it is the right thing to do), increasing the memory footprint.
- Do not pass unallocated arrays as arguments, even in those cases where they are not actually used inside the subroutine: some compilers don't like it.
- Always use
IMPLICIT NONEand declare all local variables. All variables passed as arguments to a routine should be declared asINTENT(in),(out), or(inout). All variables from modules should be explicitly specified viaUSE module, ONLY : variable. Variables used in an array declaration must be declared first, as in the following example:
INTEGER, INTENT(in) :: N
REAL(KIND = DP), INTENT(out) :: A(N)in this order (some compilers complain if you put the second line before the first).
Explanation for some of the rules:
- The reason for two and not three space indentation is because in some cases, one can end up with a lot of
enclosed
DOloops (in some case this is justified) and two indentation is the enough to clearly see the indentation. - The reason for space around
=sign like inKIND = DPrather thanKIND=DPis to be consistent. The idea is that space is put around all mathematical symbols like+,-,=, etc. - The reason for
KIND = DPand not justDPis to make is explicit that it is a kind.
Edited by giannozz