Style
In this section we describe the style conventions and guidelines for SUNDIALS source code.
Formatting
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. The .clang-format file in the
root of the project defines our configuration for clang-format. We use the
default fprettify settings, except we use 2-space indentation. The
.cmake-format.py file in the root of the project defines our configuration
for cmake-format. We also use the default black settings.
To apply clang-format, fprettify, cmake-format, and black you
can run:
./scripts/format.sh <path to directories or files to format>
Warning
The output of clang-format is sensitive to the clang-format version. We recommend
that you use version 17.0.4, which can be installed from source or with Spack. Alternatively,
when you open a pull request on GitHub, an action will run clang-format on the code. If any
formatting is required, the action will fail. Commenting with the magic keyword /autofix will
kick off a GitHub action which will automatically apply the formatting changes needed.
If clang-format breaks lines in a way that is unreadable, use // to break the line. For example,
sometimes (mostly in C++ code) you may have code like this:
MyClass::callAFunctionOfSorts::doSomething().doAnotherThing().doSomethingElse();
That you would like to format as (for readability):
MyObject::callAFunctionOfSorts()
.doSomething()
.doAnotherThing()
.doSomethingElse();
Clang-format might produce something like:
MyObject::callAFunctionOfSorts().doSomething().doAnotherThing()
.doSomethingElse();
unless you add the //
MyObject::callAFunctionOfSorts()
.doSomething() //
.doAnotherThing() //
.doSomethingElse(); //
There are other scenarios (e.g., a function call with a lot of parameters) where doing this type of line break is useful for readability too.
Output
For consistent formatting of sunrealtype, the following macros are
available.
-
SUN_FORMAT_E
A format specifier for scientific notation. This should be used when displaying arrays, matrices, and tables where fixed width alignment aids with readability.
Example usage:
for (i = 0; i < N; i++) { fprintf(outfile, SUN_FORMAT_E "\n", xd[i]); }
-
SUN_FORMAT_G
A format specifier for scientific or standard notation, whichever is more compact. It is more reader-friendly than
SUN_FORMAT_Eand should be used in all cases not covered by that macro.Example usage:
SUNLogInfo(sunctx->logger, "label", "x = " SUN_FORMAT_G, x);
-
SUN_FORMAT_SG
Like
SUN_FORMAT_Gbut with a leading plus or minus sign.
To aid in printing statistics in functions like CVodePrintAllStats(),
the following utility functions are available.
-
void sunfprintf_real(FILE *fp, SUNOutputFormat fmt, sunbooleantype start, const char *name, sunrealtype value)
Writes a
sunrealtypevalue to a file pointer using the specified format.
-
void sunfprintf_long(FILE *fp, SUNOutputFormat fmt, sunbooleantype start, const char *name, long value)
Writes a long value to a file pointer using the specified format.
-
void sunfprintf_long_array(FILE *fp, SUNOutputFormat fmt, sunbooleantype start, const char *name, long *value, size_t count)
Writes an array of long values to a file pointer using the specified format.
Logging
Use the macros below to add informational and debugging messages to SUNDIALS
code rather than adding #ifdef SUNDIALS_LOGGING_<level> / #endif blocks
containing calls to SUNLogger_QueueMsg(). Error and warning messages are
handled through package-specific ProcessError functions or the SUNAssert
and SUNCheck macros.
The logging macros help ensure messages follow the required format presented in
§1.6.1 and used by the suntools Python module
for parsing logging output. For informational and debugging output the log
message payload (the part after the brackets) must be either be a
comma-separated list of key-value pairs with the key and value separated by an
equals sign with a space on either side e.g.,
/* log an informational message */
SUNLogInfo(sunctx->logger, "begin-step", "t = " SUN_FORMAT_G ", h = " SUN_FORMAT_G, t, h);
/* log a debugging message */
SUNLogDebug(sunctx->logger, "error-estimates",
"eqm1 = " SUN_FORMAT_G ", eq = " SUN_FORMAT_G ", eqp1 = " SUN_FORMAT_G,
eqm1, eq, eqp1);
or the name of a vector/array followed by (:) = with each vector/array entry
written to a separate line e.g., a vector may be logged with
SUNLogExtraDebugVec(sunctx->logger, "new-solution", ynew, "ynew(:) =");
where the message can contain format specifiers e.g., if Fe is an array of
vectors you may use
SUNLogExtraDebugVec(sunctx->logger, "new-solution", Fe[i], "Fe_%d(:) =", i);
To assist in parsing logging messages, begin- and end- markers are used
in the log message label field to denote where particular regions begin and
end. When adding a new begin- / end- label the logs.py script will
need to be updated accordingly. The region markers currently supported by the
Python module for parsing log files are as follows:
begin-step-attempt/end-step-attemptbegin-nonlinear-solve/end-nonlinear-solvebegin-nonlinear-iterate/end-nonlinear-iteratebegin-linear-solve/end-linear-solvebegin-linear-iterate/end-linear-iteratebegin-group/end-groupbegin-stage/end-stagebegin-fast-steps/end-fast-stepsbegin-mass-linear-solve/end-mass-linear-solvebegin-compute-solution/end-compute-solutionbegin-compute-embedding/end-compute-embedding
Logging Macros
Added in version 7.2.0.
To log informational messages use the following macros:
-
SUNLogInfo(logger, label, msg_txt, ...)
When information logging is enabled this macro expands to a call to
SUNLogger_QueueMsg()to log an informational message. Otherwise, this expands to nothing.- Parameters:
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogInfoIf(condition, logger, label, msg_txt, ...)
When information logging is enabled this macro expands to a conditional call to
SUNLogger_QueueMsg()to log an informational message. Otherwise, this expands to nothing.- Parameters:
condition – a boolean expression that determines if the log message should be queued.
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format. specifiers.... – the arguments for format specifiers in
msg_txt.
To log debugging messages use the following macros:
-
SUNLogDebug(logger, label, msg_txt, ...)
When debugging logging is enabled this macro expands to a call to
SUNLogger_QueueMsg()to log a debug message. Otherwise, this expands to nothing.- Parameters:
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format. specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogDebugIf(condition, logger, label, msg_txt, ...)
When debugging logging is enabled this macro expands to a conditional call to
SUNLogger_QueueMsg()to log a debug message. Otherwise, this expands to nothing.- Parameters:
condition – a boolean expression that determines if the log message should be queued.
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format. specifiers.... – the arguments for format specifiers in
msg_txt.
To log extra debugging messages use the following macros:
-
SUNLogExtraDebug(logger, label, msg_txt, ...)
When extra debugging logging is enabled, this macro expands to a call to
SUNLogger_QueueMsg()to log an extra debug message. Otherwise, this expands to nothing.- Parameters:
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogExtraDebugIf(condition, logger, label, msg_txt, ...)
When extra debugging logging is enabled, this macro expands to a conditional call to
SUNLogger_QueueMsg()to log an extra debug message. Otherwise, this expands to nothing.- Parameters:
condition – a boolean expression that determines if the log message should be queued.
logger – the
SUNLoggerto handle the message.label – the
const char*message label.msg_txt – the
const char*message text, may contain format specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogExtraDebugVec(logger, label, vec, msg_txt, ...)
When extra debugging logging is enabled, this macro expands to a call to
SUNLogger_QueueMsg()andN_VPrintFile()to log an extra debug message and output the vector data. Otherwise, this expands to nothing.- Parameters:
logger – the
SUNLoggerto handle the message.label – the
const char*message label.vec – the
N_Vectorto print.msg_txt – the
const char*message text, may contain format specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogExtraDebugVecIf(condition, logger, label, vec, msg_txt, ...)
When extra debugging logging is enabled, this macro expands to a conditional call to
SUNLogger_QueueMsg()andN_VPrintFile()to log an extra debug message and output the vector data. Otherwise, this expands to nothing.- Parameters:
condition – a boolean expression that determines if the log message should be queued.
logger – the
SUNLoggerto handle the message.label – the
const char*message label.vec – the
N_Vectorto print.msg_txt – the
const char*message text, may contain format specifiers.... – the arguments for format specifiers in
msg_txt.
-
SUNLogExtraDebugVecArray(logger, label, nvecs, vecs, msg_txt)
When extra debugging logging is enabled, this macro expands to a loop calling
SUNLogger_QueueMsg()andN_VPrintFile()for each vector in the vector array to log an extra debug message and output the vector data. Otherwise, this expands to nothing.- Parameters:
logger – the
SUNLoggerto handle the message.label – the
const char*message label.nvecs – the
intnumber of vectors to print.vecs – the
N_Vector*(vector array) to print.msg_txt – the
const char*message text, must contain a format specifier for the vector array index.
Warning
The input parameter
msg_txtmust include a format specifier for the vector array index (of typeint) only e.g.,SUNLogExtraDebugVecArray(logger, "YS-vector-array", "YS[%d](:) =", YS, 5);
Struct Accessor Macros
Since many SUNDIALS structs use a type-erased (i.e., void*) “content” pointer, a common idiom occurring in SUNDIALS code is extracting the content, casting it to its original type, and then accessing the struct member of interest. To ensure readability, it is recommended to use locally (to the source file in question) defined macros GET_CONTENT and IMPL_MEMBER like the following example:
#define GET_CONTENT(S) ((SUNAdjointCheckpointScheme_Fixed_Content)S->content)
#define IMPL_MEMBER(S, prop) (GET_CONTENT(S)->prop)
SUNAdjointCheckpointScheme self;
IMPL_MEMBER(self, current_insert_step_node) = step_data_node;
IMPL_MEMBER(self, step_num_of_current_insert) = step_num;