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.