Fortran is case insensitive: FUNCTION means the same as function. Please write all Fortran constructs in lower case. This includes, but is not limited to, the following: program, module, type, class, intent, if, do, case, select, subroutine, function, pure, elemental, recursive, integer, real, and logical. Do not, under any circumstance, write in all-caps. All-caps things are hard to read and hard to type.
Write intrinsic math functions in lower case: sqrt, exp, log, nint, max, mod, modulo, pack, count, etc.
For certain situations, the mathematical notation distinguishes between the upper and lower case. The Gamma instead of gamma.
Use the same rules for user-defined symbols.
Use snake_case, not camelCase. The latter is notoriously difficult to read, especially for long, descriptive names.
Use common sense when writing descriptive names. If it is clear from the context of the code, using i, j, and k to denote Cartesian indices is perfectly fine. You definitely should not write Cartesian_index_in_direction_x_hat! That’s just silly. Similarly, linspace and derivx are fine. On the other hand, it is better to write field_coupling_term instead of fieldcouplingterm. Most definitely, do not write monstrosities like fcouptrm.
Your code should be properly indented. The following is acceptable:
do j = 1, size(B)
C(:, j) = A(:)*B(j)
end dowhile the following is not:
do j = 1, size(B)
C(:, j) = A(:)*B(j)
end doA good text editor allows selecting a region and pressing TAB or something similar to automatically apply proper indentation. If your editor does not do that, get a better editor. (Here’s the greatest text editor of all time - Emacs. You’re welcome!) Set your TAB to 2 spaces for the indentation level under program, module, subroutine, and function, and to 3 spaces for if and do.
Write code the way you would write an essay. Use spaces and linebreaks to improve readability and separate conceptually distinct sections of the calculation. Below are some examples.
The following is more readable
pure elemental real(r64) function Fermi(e, chempot, T)
!! e Energy in eV
!! chempot Chemical potential in eV
!! T temperature in K
real(r64), intent(in) :: e, chempot, T
Fermi = 1.0_r64/(exp((e - chempot)/kB/T) + 1.0_r64)
end function Fermicompared to
pure elemental real(r64) function Fermi(e, chempot, T)
!! e Energy in eV
!! chempot Chemical potential in eV
!! T temperature in K
real(r64), intent(in) :: e, chempot, T
Fermi = 1.0_r64/(exp((e - chempot)/kB/T) + 1.0_r64)
end function FermiThe former reads better because conceptually different sections of the code are separated by a line break.
The following is an acceptable use of spaces around mathematical operations:
fx = sin(x)/(1 + (2 - x**2))while the following is not:
fx=sin( x ) / (1+(2-x**2))Several issues with the second code snippet. When we do math on paper, we write + and -, but not around *, /, and **.
Use comma like you would in writing. So, write thing1, thing2, not thing1,thing2.
Following are some examples where one or more rules have been broken in the second column. Try to identify all the issues.
| do | don’t |
|---|---|
if(condition) | if( condition ) |
do i = 1, 3 | do i=1,3 |
call do_magic(hat, bunny) | call do_magic (hat,bunny) |
real(r64), intent(in) :: data(:, :) | real(r64), intent(in) :: data(:,:) |
interface operator(.umklapp.) | interface operator( .umklapp. ) |
use params, only: kB, twopi | use params,only : kB,twopi |
Use modern notation everywhere.
| do | don’t |
|---|---|
> and >= | .gt. and .ge. |
< and <= | .lt. and .le. |
== | .eq. |
/= | .ne. |
[1, 2, 3] | (/1, 2, 3/) |
Keep your code brief by using a functional style over an imperative style. It is preferable to write
!Assume A, B, and C are NxN matrices and m is a scalar.
A = A*B !elementwise multiplication
C = A + B !elementwise addition
C = m*C !elementwise multiplication with a scalarinstead of
!Assume A, B, and C are NxN matrices and m is a scalar.
N = size(A, 1)
do j = 1, N
do i = 1, N
A(i, j) = A(i, j)*B(i, j)
end do
end do
do j = 1, N
do i = 1, N
C(i, j) = A(i, j) + B(i, j)
end do
end do
do j = 1, N
do i = 1, N
C(i, j) = m*C(i, j)
end do
end doIt is rather obvious why the first is preferable.
Fortran arrays are saved in a column major manner. C, on the other hand, is row major. Mind the difference when you access array slices.
Instead of
do j = 1, N
do i = 1, N
if(use_special_algo) then
y = function_special(i, j)
else
y = function_boring(i, j)
end if
end do
end doconsider pulling out the conditional outside by doing
function_pointer => function_boring
if(use_special_algo) function_pointer => function_special
do j = 1, N
do i = 1, N
y = function_pointer(i, j)
end do
end do
nullify(function_pointer)In order to do the latter, you will have to define an abstract interface for function_special and function_boring. Remember to nullify any associated pointers before exiting a procedure.
For condition based switching, use select case over if and else if. So do
select case(particle)
case('el')
print*, "I'm an electron."
case('ph')
print*, "I'm a phonon."
case('pl')
print*, "I'm a plasmon."
case default
print*, "I don't know who I am."
end selectinstead of
if(particle == 'el') then
print*, "I'm an electron."
else if(particle == 'ph') then
print*, "I'm a phonon."
else if(particle == 'pl') then
print*, "I'm a plasmon."
else
print*, "I don't know who I am."
end ifEvery program and module should have implicit none declared up top.
Please.
Always specify exactly what you need from a module. That is, always do use module_something, only: thing1, thing2 instead of just use module_something.
No other variable should be. Subroutines should not access global variables. Write pure subroutines and functions whenever possible. Strictly control data access and modification rights with the intent keyword.
Use objects but don’t write Java. Use the functional style but don’t try to mimic Haskell. The style we use can be called object-based procedural. Try to write pure procedures whenever possible. Try to strike a good balance between performance, readability, and extensibility. Think La Sagrada Familia, not the Sistine Chapel.
If you see any of these rules broken near the part of the code you are touching, please go ahead and fix it.