Coding style

MESA has been developed over many years, thus there many different styles of code in use. We do not require old code to be ported to the new style. However, new code should be written following these guidelines.

Whitespace

In free form Fortran files, indentation is 3 spaces.

Indentation/whitespace rules are codified in an EditorConfig file located in the root of the repository (.editorconfig).

Fortran

The preferred Fortran version is at least Fortran 90 as this enables things like modules and allocatable arrays.

Common and equivalence blocks are banned. Just put the variables in a module header if needed to be shared.

Exponents

When raising a number to a floating-point power, use the pow() routine provided by math_lib:

pow(x,1.5d0)

instead of

x**1.5d0

Use powN(x) to raise x to an integer power N. For real types you should always use these for integer powers greater than 2. **2 has so far not been a problem and is tolerated though not recommended. For auto_diff types you must use powN for integer powers including 2. Doing otherwise will result in a compiler error.

Errors

In general a subroutine should return as its last argument a integer ierr which denotes whether the subroutine succeeded or not. A value of ierr=0 is to be used to show the subroutine succeeded, any other value of ierr denotes an error. Any meaning in different non-zero values of ierr can only be determined by inspecting the subroutine that set the ierr.

The typical way to handle things is:

integer, intent(out) :: ierr
ierr = 0

call my_subroutine(x,ierr)
if (ierr /= 0) return

for non-critical errors.

Stop

When signalling a critical error which can not be recovered from you should use:

use utils_lib, only: mesa_error

call mesa_error(__FILE__,__LINE__)

which will generate a backtrace where the error occurs, which aids in debugging. The subroutine mesa_error can also have a optional error message:

use utils_lib, only: mesa_error

call mesa_error(__FILE__,__LINE__,"An error occurred")

Do not use:

stop 1

as it can become difficult to distinguish where in the code called stop if multiple functions use stop 1.

Note that the __FILE__ and __LINE__ macros are not expanded in files that are included using Fortran’s include statement.

Doubles

The preferred manner to declare a double precision variable is:

real(dp) :: x

instead of

double precision :: x

When using a numerical value in an expression you must make sure it is evaluated as a double. Thus use:

y1 = 1.1d0 * x
! or
y2 = 1.1_dp * x

Do not leave values as a bare float:

y3 = 1.1 * x

As the 1.1 gets interpreted as a single precision value, and will lead y3 to have a different value to y1 or y2.

OMP critical blocks

OMP critical blocks allow the programmer to specify that a section of code should only be executed by one thread at a time. They can also be given a name:

!$omp critical my_block

and this name should differ from any other code entities (e.g. subroutines).

Each named critical block will be executed by one thread at a time. Different named critical blocks can be executed at the same time. However, all unnamed critical blocks act like one block and thus can not be executed in parallel. Therefore you should always named your OMP critical blocks to ensure the best performance.

Do not name your OMP critical block with a name that has already been used for a variable, procedure, module or any other object.

Formatting

Use explicit formats for any write statements. Different compilers use different default formats, which can lead to spurious failures when strings are compared. e.g. when printing some floating point number x, instead of

write(*,*) x

use

write(*, '(1pd26.16)') x

Unformatted statements are likely to cause unit tests to fail. They also make it difficult to compare output from runs with different compilers.

Some helpful formats are provided in include/formats.

Unformatted reads

read(unit,*) x,y,z

Should be avoided when the variables that are strings. This is because if the string contains a / (forward-slash) then when doing a unformatted read Fortran will stop reading the line.

Either build a full format statement or read the line into one string and split on whitespace. There is also a function split_line in utils_lib.f90 that can be used to split a line up based on whitespace.

Constants

The const module defines many commonly used mathematical (e.g. pi) and physical constants (e.g. hbar), which should be used for consistency across the code. This includes simple fractions (e.g. one_third) and simple functions of mathematical constants (e.g. sqrt2, pi4 = 4*pi).

Constants should be added to const_def.f90 if they meet any of the following criteria:

  • If it is a well known mathematical or physical constant or derived from other well known constants

  • If other code might use the constant

Environment variables

If making a new environment variable then the variable should be prefixed with MESA_ to ensure we don’t collide with other variables.