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_E
and 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_G
but 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
sunrealtype
value 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-attempt
begin-nonlinear-solve
/end-nonlinear-solve
begin-nonlinear-iterate
/end-nonlinear-iterate
begin-linear-solve
/end-linear-solve
begin-linear-iterate
/end-linear-iterate
begin-group
/end-group
begin-stage
/end-stage
begin-fast-steps
/end-fast-steps
begin-mass-linear-solve
/end-mass-linear-solve
begin-compute-solution
/end-compute-solution
begin-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
SUNLogger
to 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
SUNLogger
to 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
SUNLogger
to 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
SUNLogger
to 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
SUNLogger
to 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
SUNLogger
to 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
SUNLogger
to handle the message.label – the
const char*
message label.vec – the
N_Vector
to 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
SUNLogger
to handle the message.label – the
const char*
message label.vec – the
N_Vector
to 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
SUNLogger
to handle the message.label – the
const char*
message label.nvecs – the
int
number 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_txt
must 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;