Coding Guidelines¶
Indent with two spaces per indentation level. Do not use tabs.
Keep lines strictly under 80 characters.
Use American English spelling.
Vertical alignment is important!
Separate subroutines/functions with exactly two blank lines. There should never be more than two consecutive blank lines in any Fortran file.
Format a subroutine as in the following examples:
subroutine read_spectrum(spectrum, filename, freq_unit) type(pulse_t), intent(inout) :: spectrum character(len=*), intent(in) :: filename character(len=*), optional, intent(in) :: freq_unit character(len=unit_l) :: file_freq_unit real(idp), allocatable :: w(:), spectrum_real(:), spectrum_imag(:) integer :: i, nt real(idp) :: wmax real(idp) :: dt character (len=error_l) :: routine_name = 'read_spectrum' call add_backtrace(module_name, routine_name) [...] call del_backtrace() end subroutine read_spectrum subroutine getanrelax(an, n, alpha, nalpha) integer, intent(in) :: n real(idp), intent(inout) :: an(0:n) real(idp), intent(in) :: alpha integer, intent(inout) :: nalpha [...] end subroutine getanrelax
After the subroutine definition, there should be a blank line, followed by the declaration of the dummy variables.
The dummy variables must be declared in the same order as they appear in the subroutine definition, with one variable per line. The only exception are certain explicit shape declarations, c.f. the example for
getanrelax
above, where the Fortran standard demands thatn
is defined beforean
. Intent must be declared for all variables. Theoptional
keyword, intents, and the double-colons should all be lined up vertically.Any dummy variable of type
character
must be declared withlen=*
.After the declaration of the dummy variables, there must be a blank line, followed by the declaration of the local variables.
After the declaration of the local variables, the
routine_name
should be set and the routine should be added to the backtrace. This can be omitted for routines that are performance-critical.If a backtrace entry was added in the beginning of the routine, it should be deleted again at the end of the routine. Note that if you have other exit-points in the routine (
return
statements), you must make sure thatdel_backtrace
is called.The
end subroutine
line must include the subroutine name.
Logical sections within a routine should be separated by single blank lines within one routine. There must never be more than one consecutive blank line inside of a routine.
If there are blocks within a loop, the loop
do
, andend do
must also be surrounded by blank lines:! [...] iter = para%oct%iter_start octloop: do ! Read dynamically chaning OCT params call read_oct_params(para, pulses01(:,S0)) ![...] if (iter > para%oct%iter_stop) exit octloop ! calculate co-states call get_chis(chis, fw_states_T, targets) ! [...] ! Cleanup ! [...] end do octloop ! [...]
The same holds for
if
or other block statements.If there are many levels of nested loops, or if the loop body is longer than about 40 lines, name the loop (
octloop
in the example above).For long or nested
if
blocks, consider adding comment to theelse
clausesif (targets(s)%in_liouville_space) then if (complex_flag) then ! [...] else ! [...] end if else ! Hilbert space if (complex_flag) then ! [...] else ! [...] end if end if
Generally, continued lines should have the continuation marker
&
at the beginning (not just at the end of the continued line.)if (complex_flag) then fw_temp_imag(s) = fw_temp(s) call dHdP_psi(fw_temp(s)%psi, fw_states(s)%psi, targets(s)%ham, & & pulses0, id, ti, add=.false., & & hpsi_imag = fw_temp_imag(s)%psi) else call dHdP_psi(fw_temp(s)%psi, fw_states(s)%psi, targets(s)%ham, & & pulses0, id, ti, add=.false.) end if
The continuation symbols must line up to indicate the indentation level. In the above example, the
&
must line up under thec
ofcall
. After the continuation symbol, further spaces may be used to line up code vertically.Generally, put the continuation mark at the end of a continued line in column 80 (so that all the continuation marks at the right edge of a file line up vertically). This is of course not possible if a line is continued within a string. You may also relax this rule if it improves readability.
Do not put spaces around equal signs when passing variables in a
call
:call diag_complex_matrix(hessenberg(1:m,1:m), ev, sort=.true.)
Always put spaces around equal signs in assignments:
m0 = m - 1
Generally, commas and operators (
+
,-
,==
, etc.) should have spaces around them. Array indices should not contain spaces (hessenberg(1:m,1:m)
).When importing routines from other modules, import only the required routines:
use inout_mod, only: nr2str, lower_case use kin_mod, only: kinop_psi
Only the
global_mod
,def_mod
, anderror_mod
should be used withoutonly
.Modules should be formatted as in the following example:
module qdyn_ham_mod !! @description: [...] use global_mod use def_mod use error_mod use inout_mod, only: nr2str, lower_case ![...] implicit none public :: init_ham, H_psi, H_rho, L_rho, arnoldi, extend_arnoldi, & get_ritz_eigenvals, init_ops, dHdP_psi, dLdP_rho ! [...] private character (len=error_l) :: module_name = 'qdyn_ham_mod' contains ! [... (subroutines) ...] end module
Make sure that every program or module contains the
implicit none
statement.Only high level subroutines should be
public
. Low level routines, which a user should not use independently, should beprivate
.Do not use pointers unless absolutely necessary (usually,
allocatable
will do the job).Never write something like
if (a .eqv. .true.)
. This should be written asif (a)
.Every routine and data structure must be documented.
Every module must be appropriately tested.
Always check the error code from allocations. Every call to
allocate
should be followed by a call toallocerror
.Always check the error code from I/O calls. All calls to
read
/write
should be followed by the appropriate error handlers.Do not use constants for file unit numbers. Always use the file management provided by the
fileman
module. This avoids problems in multi-threaded programs.All calculations must be performed in an internal unit system. Conversion to/from physical units must only happen in input and output. See the notes on the unit system. You must follow the conventions for routines reading and writing data files described there.
Code Review Guidelines¶
Differentiate between subjective and objective feedback and indicate it as such. Communicate which ideas you feel passionate about and those you don’t. Be kind and don’t forget to praise what you like.
In QDYN we aim for a compromise between code efficiency and readability. For high level routines the latter is more important, while low level routines may be more performance critical. Use your best judgment or consult with a team member. In any case, if you don’t understand a piece of code, say so. There’s a good chance that someone else would be confused as well.
As a guideline, you can use the following checklist for doing code reviews:
No potential problems with the code (bugs and maintainability)
Changes balance code efficiency and readability
Comments clarify complicated code blocks
All routines are properly documented according to the documentation checklist
New features or modules come with an how-to and/or example
How-tos, examples and topical-overviews are written according to their corresponding documentation checklist
New features are covered by a test
Code changes meet the coding guidelines
Setting Up Your Editor¶
Vim¶
If you are using vim or neovim, make sure your ~/.vimrc
contains the line:
filetype plugin on
and put the following in either ~/.vim/ftplugin/fortran.vim
or
~/.vim/after/ftplugin/fortran.vim
:
setlocal textwidth=80
setlocal tabstop=2
setlocal shiftwidth=2
setlocal shiftround
setlocal expandtab
setlocal spellcapcheck=
" recognize any number of !'s as a single comment marker
" This means you can e.g. reflow QDYN docstrings blocks starting with '!! '
" with the gq shortcut.
setlocal comments=n:!
An example vim configuration can be found at https://github.com/goerz/vimrc
Emacs¶
If you are using emacs, add the following to your ~/.emacs
file:
(setq f90-associate-indent 2)
(setq f90-auto-keyword-case (quote downcase-word))
(setq f90-comment-region "!!!")
(setq f90-indented-comment-re "!!?" )
(setq f90-do-indent 2)
(setq f90-if-indent 2)
(setq f90-program-indent 2)
(setq f90-type-indent 2)
(setq f90-continuation-indent 0)
(setq fortran-line-length 80)