VPLanet Contributors Guide
Naming conventions
Variable and function names use a “Hungarian style”. Variables names should have the following prefixes:
b = Boolean
i = Integer
d = Double
s = String
ba = Boolean Array
ia = Integer Array
da = Double Array
sa = String Array
pb = Pointer to Boolean
pi = Pointer to Integer
pd = Pointer to Double
pab = Pointer to Boolean Array
pai = Pointer to Integer Array
pad = Pointer to Double Array
fnp = Function Pointer
Function names should have the following prefixes:
fv = Void function
fb = Boolean Function (returns 0 or 1)
fi = Integer Function (returns int)
fd = Double Function (returns double)
fs = String function (returns char[])
fba = Boolean Array Function
fia = Integer Array Function
fda = Double Array Function
fsa = String Array Function (returns char[][])
EXCEPT the following when they have either a variable or module name after them: BodyCopy, PropsAux, ForceBehavior, CountHalts, Read, Write, AddModule, Verify, InitializeOptions, InitializeOutput, InitializeBody, InitializeUpdate, InitializeControl, InitializeHalts, InitializeModule, InitializeConstants.
C Code formatting
Use no tab characters and indent blocks by 2 spaces.
All conditional blocks should have a { at the end of the line.
The closing } is on the line following the conditional block.
Single line conditionals must be surrounded by {}.
All functions shall return a variable, never an algebraic expression.
All lines shall be less than 80 characters.
For expressions that span multiple lines, the second and following lines shall have additional indents.
Lines that continue to the next must end with a “" in C files.
Variables cannot be assigned a value at declaration.
No unused variables in a function.
Note
The top-level directory contains the file .clang-format that contains instructions for a program to enforce some C code formatting with the clangformat package. You can code up your changes and then run this command afterwards to fix many (but not all) formatting problems.
Code Coverage
We use gcov and
lcov to check code
coverage by the test scripts. In general, there will always exist
lines of code that you don’t want to have executed – such as lines
whose only purpose is to catch an error, print a message to screen, and
terminate the program. But gcov doesn’t know that, so if your tests
don’t hit those lines, your coverage goes down. The way around this is to
use the LCOV_EXCL_LINE
, LCOV_EXCL_START
, and
LCOV_EXCL_END
commands to explicitly markup lines you don’t want
included in the coverage report. For instance, consider this snippet
of code inside ReadXFrac
, a function that reads in the user’s value
for the XUV radius fraction in atmesc
:
1AddOptionDouble(files->Infile[iFile].cIn,options->cName,&dTmp,&lTmp,
2 control->Io.iVerbose);
3if (lTmp >= 0) {
4 NotPrimaryInput(iFile,options->cName,files->Infile[iFile].cIn,lTmp,
5 control->Io.iVerbose);
6 if (dTmp < 0) {
7 if (control->Io.iVerbose >= VERBERR) {
8 fprintf(stderr,"ERROR: %s must be >= 0.\n",options->cName);
9 }
10 LineExit(files->Infile[iFile].cIn,lTmp);
11 }
12 body[iFile-1].dXFrac = dTmp;
13 UpdateFoundOption(&files->Infile[iFile],options,lTmp,iFile);
14}
Lines 5, 6, and 7 are only ever executed if the user provides a negative XUV fraction. You could in principle add a test that checks whether the error handling is working, but that seems silly. It’s easiest to just mark these lines so that gcov and lcov know to skip them. You can either mark each line:
1AddOptionDouble(files->Infile[iFile].cIn,options->cName,&dTmp,&lTmp,
2 control->Io.iVerbose);
3if (lTmp >= 0) {
4 NotPrimaryInput(iFile,options->cName,files->Infile[iFile].cIn,lTmp,
5 control->Io.iVerbose);
6 if (dTmp < 0) {
7 if (control->Io.iVerbose >= VERBERR) { // LCOV_EXCL_LINE
8 fprintf(stderr,"ERROR: %s must be >= 0.\n",options->cName); // LCOV_EXCL_LINE
9 }
10 LineExit(files->Infile[iFile].cIn,lTmp); // LCOV_EXCL_LINE
11 }
12 body[iFile-1].dXFrac = dTmp;
13 UpdateFoundOption(&files->Infile[iFile],options,lTmp,iFile);
14}
or use a block:
1AddOptionDouble(files->Infile[iFile].cIn,options->cName,&dTmp,&lTmp,
2 control->Io.iVerbose);
3if (lTmp >= 0) {
4 NotPrimaryInput(iFile,options->cName,files->Infile[iFile].cIn,lTmp,
5 control->Io.iVerbose);
6 if (dTmp < 0) {
7 // LCOV_EXCL_START
8 if (control->Io.iVerbose >= VERBERR) {
9 fprintf(stderr,"ERROR: %s must be >= 0.\n",options->cName);
10 }
11 LineExit(files->Infile[iFile].cIn,lTmp);
12 // LCOV_EXCL_STOP
13 }
14 body[iFile-1].dXFrac = dTmp;
15 UpdateFoundOption(&files->Infile[iFile],options,lTmp,iFile);
16}
Miscellaneous
Do not use the word test in any file/folder names unless you want it to be part of the unit tests, as the unit tester collects and attempts to run any file with test in it.
Use spaces, not tabs, when indenting!
Do not use the pound sign “#” in comments, as this refers to a link in Doxygen.
Use the PEP 8 guide for python scripts: https://www.python.org/dev/peps/pep-0008/
Comments
File headers
All files should begin with a doxygen-style docstring:
Functions
All functions should have docstrings describing what they are with one-liners describing each of the inputs / outputs.
Macros and struct members
Struct members and constants declared using the
#define
directive should all have inline comments for doxygen following this syntax:and
Fancy stuff
We are currently using breathe to link DOXYGEN documentation to sphinx html output. This means you should familiarize yourself with both the DOXYGEN markup and the reStructuredText markup.
The things you can do with DOXYGEN + breathe are fairly limited, so it’s useful to know that you can always take advantage of reStructuredText commands by escaping a paragraph with the
rst
command. For instance, check out the header in the atmesc source:The DOXYGEN citation functionality is pretty limited, but should improve. For now you can just copy/past the URL into the comments.