Coding Conventions and Rules
These rules should be followed for all new code. Unfortunately, old code might not adhere to all of these rules.
Do not use language features that are not compatible with C99, C++14, and MSVC v1900+ (Visual Studio 2015). Examples of such features include variable-length arrays. Exceptions are allowed when interfacing with a library which requires a newer standard.
All new code added to SUNDIALS should be formatted with clang-format for C/C++, fprettify for Fortran, cmake-format for CMake, and black for Python. See Style for details.
Spaces not tabs.
Comments should use proper spelling and grammar.
Following the Google Style Guide, TODO comments are used to note code that is “temporary, a short-term solution, or good-enough but not perfect.”
A consistent TODO comment format provides an easy to search for keyword with details on how to get more information. TODO comments should start with
TODO
followed by a unique identifier, enclosed in parentheses, for the person most knowledgeable about the issue and a brief description of the TODO item. Generally, these comments should be used sparingly and are not a substitute for creating an issue or bug report. When applicable, the comment should include the relevant issue or bug report number.Examples:
/* TODO(DJG): Update to new API in the next major release (Issue #256) */
If statements and loops should always have braces even if they are one line.
All SUNDIALS data structures should hold onto a
SUNContext
object. Exceptions are theSUNLogger
andSUNProfiler
classes.All SUNDIALS functions should return a
SUNErrCode
. Many older functions do not do this and are exceptions to the rule for backwards compatibility. In addition, internal helper functions may or may-not return aSUNErrCode
.All SUNDIALS functions, with the exception of some functions that do not have access to a
SUNContext
, should begin with a call toSUNFunctionBegin()
. The argument toSUNFunctionBegin()
is aSUNContext
which should come from the first object in the function parameter list that has aSUNContext
. This macro is used for error handling and declares a local variable access via the macroSUNCTX_
.SUNErrCode N_VLinearCombination_Serial(int nvec, realtype* c, N_Vector* X, N_Vector z) { SUNFunctionBegin(X[0]->sunctx); // Correct int i; sunindextype j, N; realtype* zd=NULL; realtype* xd=NULL; /* invalid number of vectors */ SUNAssert(nvec >= 1, SUN_ERR_ARG_OUTOFRANGE); // ... } SUNErrCode N_VLinearCombination_Serial(int nvec, realtype* c, N_Vector* X, N_Vector z) { int i; sunindextype j, N; realtype* zd=NULL; realtype* xd=NULL; SUNFunctionBegin(X[0]->sunctx); // Incorrect, SUNFunctionBegin should occur as early as possible /* invalid number of vectors */ SUNAssert(nvec >= 1, SUN_ERR_ARG_OUTOFRANGE); // ... } int CVodeGetEstLocalErrors(void *cvode_mem, N_Vector ele) { CVodeMem cv_mem; if (cvode_mem==NULL) { cvProcessError(NULL, CV_MEM_NULL, __LINE__, __func__, __FILE__, MSGCV_NO_MEM); return(CV_MEM_NULL); } cv_mem = (CVodeMem) cvode_mem; SUNFunctionBegin(cv_mem->sunctx); // Correct - this is as early as possible to call SUNFunctionBegin SUNFunctionBegin(ele->sunctx); // Incorrect - cvode_mem is first in the function parameter list // ... }
All references to
SUNContext
objects should be done via theSUNCTX_
macro. The only exceptions are functions in theSUNContext
class.All calls to SUNDIALS functions that return a
SUNErrCode
should have their return value checked with a macro from theSUNCheckCall
family. These macros are documented in the header filesundials/priv/sundials_errors_impl.h
andsundials/priv/sundials_mpi_errors_impl.h
.SUNCheckCall(N_VLinearCombination(...)); // Correct
Avoid storing the return value and then checking the stored value except when absolutely necessary.
SUNErrCode err; err = N_VLinearCombination(...); SUNCheckCall(err); // Avoid except when absolutely necessary.
All calls to SUNDIALS functions that do not return a
SUNErrCode
should be followed by checking the last error stored in theSUNContext
. The exception to this rule is for internal helper functions. These should not be checked unless they return aSUNErrCode
. These checks are done with theSUNCheckLastErr
macros.// Correct (void) N_VLinearSum(...); SUNCheckLastErr(); // Incorrect - we must check for errors in N_VDotProd before calling a second function sunrealtype norm2 = SUNRsqrt(N_VDotProd(...)); SUNCheckLastErr(); // Correct sunrealtype norm2 = N_VDotProd(...); SUNCheckLastErr(); norm2 = SUNRsqrt(norm2);
Programmer errors should be checked with the
SUNAssert
macro, that verifies whether its argument evaluates to “true”, and specifies an error flag otherwise. By programmer errors we mean, for example, illegal inputs such as mismatching dimensions or aNULL
value for something that should not be.SUNLinearSolver SUNLinSol_Band(N_Vector y, SUNMatrix A, SUNContext sunctx) { SUNFunctionBegin(sunctx); SUNLinearSolver S; SUNLinearSolverContent_Band content; sunindextype MatrixRows; // Correct - check these with SUNAssert SUNAssert(SUNMatGetID(A) == SUNMATRIX_BAND, SUN_ERR_ARG_WRONGTYPE); SUNAssert(SUNBandMatrix_Rows(A) == SUNBandMatrix_Columns(A), SUN_ERR_ARG_DIMSMISMATCH); SUNAssert(y->ops->nvgetarraypointer, SUN_ERR_ARG_INCOMPATIBLE); // ... }
Return statements should not unnecessarily use parentheses. Prefer
return x;
toreturn(x);
. Note, however, lots of older SUNDIALS source code usesreturn(x);
.Always use
sunindextype
for variables that are related to problem dimensions. E.g., use it for the length of a vector, or dimensions of a matrix. The only exception is when interfacing with a third party library requires a different variable type.Conversely, never use
sunindextype
for variables that are not specifically related to the dimensions of a vector, matrix, etc.. E.g., if you have a variable that represents the number of integer “words” allocated in a workspace do not usesunindextype
for it. Instead use the appropriate integer type (e.g.,int64_t
) directly. For counters, usesuncountertype
.Do not use unsigned integer types except for
size_t
when the value you are storing is a memory size. Unsigned integer types must never be used in parts of the SUNDIALS API that will be interfaced to Fortran since the Fortran standard does not include unsigned integers.Use the print functions, format macros, and output guidelines detailed in Output.
Follow the logging style detailed in Logging.
Use sizeof(variable) rather than sizeof(type). E.g.,
int a = 1; int array_length = 10; int* array1 = malloc(array_length * sizeof(a)); // Do this int* array2 = malloc(array_length * sizeof(int)); // Don't do this