1.4. The SUNContext Type
Added in version 6.0.0.
All of the SUNDIALS objects (vectors, linear and nonlinear solvers, matrices,
etc.) that collectively form a SUNDIALS simulation, hold a reference to a common
simulation context object defined by the SUNContext
class.
-
type SUNContext
An opaque pointer used by SUNDIALS objects for error handling, logging, profiling, etc.
Users should create a SUNContext
object prior to any other calls to
SUNDIALS library functions by calling:
-
SUNErrCode SUNContext_Create(SUNComm comm, SUNContext *sunctx)
Creates a
SUNContext
object associated with the thread of execution. The data of theSUNContext
class is private.- Parameters:
comm – the MPI communicator or
SUN_COMM_NULL
if not using MPI.sunctx – [in,out] upon successful exit, a pointer to the newly created
SUNContext
object.
- Returns:
SUNErrCode
indicating success or failure.
The created SUNContext
object should be provided to the constructor
routines for different SUNDIALS classes/modules e.g.,
SUNContext sunctx;
void* package_mem;
N_Vector x;
SUNContext_Create(SUN_COMM_NULL, &sunctx);
package_mem = CVodeCreate(..., sunctx);
package_mem = IDACreate(..., sunctx);
package_mem = KINCreate(..., sunctx);
package_mem = ARKStepCreate(..., sunctx);
x = N_VNew_<SomeVector>(..., sunctx);
After all other SUNDIALS code, the SUNContext
object should be freed
with a call to:
-
SUNErrCode SUNContext_Free(SUNContext *sunctx)
Frees the
SUNContext
object.- Parameters:
sunctx – pointer to a valid
SUNContext
object,NULL
upon successful return.
- Returns:
SUNErrCode
indicating success or failure.
Warning
When MPI is being used, the
SUNContext_Free()
must be called prior toMPI_Finalize
.
The SUNContext
API further consists of the following functions:
-
SUNErrCode SUNContext_GetLastError(SUNContext sunctx)
Gets the last error code set by a SUNDIALS function call. The function then resets the last error code to SUN_SUCCESS.
- Parameters:
sunctx – a valid
SUNContext
object.
- Returns:
the last
SUNErrCode
recorded.
-
SUNErrCode SUNContext_PeekLastError(SUNContext sunctx)
Gets the last error code set by a SUNDIALS function call. The function does not reset the last error code to SUN_SUCCESS.
- Parameters:
sunctx – a valid
SUNContext
object.
- Returns:
the last
SUNErrCode
recorded.
-
SUNErrCode SUNContext_PushErrHandler(SUNContext sunctx, SUNErrHandlerFn err_fn, void *err_user_data)
Pushes a new
SUNErrHandlerFn
onto the error handler stack so that it is called when an error occurs inside of SUNDIALS.- Parameters:
sunctx – a valid
SUNContext
object.err_fn – a callback function of type
SUNErrHandlerFn
to be pushed onto the error handler stack.err_user_data – a pointer that will be passed back to the callback function when it is called.
- Returns:
SUNErrCode
indicating success or failure.
-
SUNErrCode SUNContext_PopErrHandler(SUNContext sunctx)
Pops the last
SUNErrHandlerFn
off of the error handler stack.- Parameters:
sunctx – a valid
SUNContext
object.
- Returns:
SUNErrCode
indicating success or failure.
-
SUNErrCode SUNContext_ClearErrHandlers(SUNContext sunctx)
Clears the entire error handler stack. After doing this it is important to push an error handler onto the stack with
SUNContext_PushErrHandler
otherwise errors will be ignored.- Parameters:
sunctx – a valid
SUNContext
object.
- Returns:
SUNErrCode
indicating success or failure.
-
SUNErrCode SUNContext_GetProfiler(SUNContext sunctx, SUNProfiler *profiler)
Gets the
SUNProfiler
object associated with theSUNContext
object.- Parameters:
sunctx – a valid
SUNContext
object.profiler – [in,out] a pointer to the
SUNProfiler
object associated with this context; will beNULL
if profiling is not enabled.
- Returns:
SUNErrCode
indicating success or failure.
-
SUNErrCode SUNContext_SetProfiler(SUNContext sunctx, SUNProfiler profiler)
Sets the
SUNProfiler
object associated with theSUNContext
object.- Parameters:
sunctx – a valid
SUNContext
object.profiler – a
SUNProfiler
object to associate with this context; this is ignored if profiling is not enabled.
- Returns:
SUNErrCode
indicating success or failure.
-
SUNErrCode SUNContext_SetLogger(SUNContext sunctx, SUNLogger logger)
Sets the
SUNLogger
object associated with theSUNContext
object.- Parameters:
sunctx – a valid
SUNContext
object.logger – a
SUNLogger
object to associate with this context; this is ignored if logging is not enabled.
- Returns:
SUNErrCode
indicating success or failure.
Added in version 6.2.0.
-
SUNErrCode SUNContext_GetLogger(SUNContext sunctx, SUNLogger *logger)
Gets the
SUNLogger
object associated with theSUNContext
object.- Parameters:
sunctx – a valid
SUNContext
object.logger – [in,out] a pointer to the
SUNLogger
object associated with this context; will beNULL
if logging is not enabled.
- Returns:
SUNErrCode
indicating success or failure.
Added in version 6.2.0.
1.4.1. Implications for task-based programming and multi-threading
Applications that need to have concurrently initialized SUNDIALS simulations need to take care to understand the following:
A
SUNContext
object must only be associated with one SUNDIALS simulation (a solver object and its associated vectors etc.) at a time.Concurrently initialized is not the same as concurrently executing. Even if two SUNDIALS simulations execute sequentially, if both are initialized at the same time with the same
SUNContext
, behavior is undefined.It is OK to reuse a
SUNContext
object with another SUNDIALS simulation after the first simulation has completed and all of the simulation’s associated objects (vectors, matrices, algebraic solvers, etc.) have been destroyed.
The creation and destruction of a
SUNContext
object is cheap, especially in comparison to the cost of creating/destroying a SUNDIALS solver object.
The following (incomplete) code examples demonstrate these points using CVODE as the example SUNDIALS package.
SUNContext sunctxs[num_threads];
int cvode_initialized[num_threads];
void* cvode_mem[num_threads];
// Create
for (int i = 0; i < num_threads; i++) {
sunctxs[i] = SUNContext_Create(...);
cvode_mem[i] = CVodeCreate(..., sunctxs[i]);
cvode_initialized[i] = 0; // not yet initialized
// set optional cvode inputs...
}
// Solve
#pragma omp parallel for
for (int i = 0; i < num_problems; i++) {
int retval = 0;
int tid = omp_get_thread_num();
if (!cvode_initialized[tid]) {
retval = CVodeInit(cvode_mem[tid], ...);
cvode_initialized[tid] = 1;
} else {
retval = CVodeReInit(cvode_mem[tid], ...);
}
CVode(cvode_mem[i], ...);
}
// Destroy
for (int i = 0; i < num_threads; i++) {
// get optional cvode outputs...
CVodeFree(&cvode_mem[i]);
SUNContext_Free(&sunctxs[i]);
}
Since each thread has its own unique CVODE and SUNContext object pair, there
should be no thread-safety issues. Users should be sure that you apply the same
idea to the other SUNDIALS objects needed as well (e.g. an N_Vector
).
The variation of the above code example demonstrates another possible approach:
// Create, Solve, Destroy
#pragma omp parallel for
for (int i = 0; i < num_problems; i++) {
int retval = 0;
void* cvode_mem;
SUNContext sunctx;
sunctx = SUNContext_Create(...);
cvode_mem = CVodeCreate(..., sunctx);
retval = CVodeInit(cvode_mem, ...);
// set optional cvode inputs...
CVode(cvode_mem, ...);
// get optional cvode outputs...
CVodeFree(&cvode_mem);
SUNContext_Free(&sunctx);
}
So long as the overhead of creating/destroying the CVODE object is small
compared to the cost of solving the ODE, this approach is a fine alternative to
the first approach since SUNContext_Create()
and
SUNContext_Free()
are much cheaper than the CVODE create/free routines.
1.4.2. Convenience class for C++ Users
For C++ users a RAII safe class, sundials::Context
, is provided:
namespace sundials {
class Context : public sundials::ConvertibleTo<SUNContext>
{
public:
explicit Context(SUNComm comm = SUN_COMM_NULL)
{
sunctx_ = std::make_unique<SUNContext>();
SUNContext_Create(comm, sunctx_.get());
}
/* disallow copy, but allow move construction */
Context(const Context&) = delete;
Context(Context&&) = default;
/* disallow copy, but allow move operators */
Context& operator=(const Context&) = delete;
Context& operator=(Context&&) = default;
SUNContext Convert() override
{
return *sunctx_.get();
}
SUNContext Convert() const override
{
return *sunctx_.get();
}
operator SUNContext() override
{
return *sunctx_.get();
}
operator SUNContext() const override
{
return *sunctx_.get();
}
~Context()
{
if (sunctx_) SUNContext_Free(sunctx_.get());
}
private:
std::unique_ptr<SUNContext> sunctx_;
};
} // namespace sundials