1.3. 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
SUNContextobject associated with the thread of execution. The data of theSUNContextclass is private.- Parameters:
comm – the MPI communicator or
SUN_COMM_NULLif not using MPI.sunctx – [in,out] upon successful exit, a pointer to the newly created
SUNContextobject.
- Returns:
SUNErrCodeindicating 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
SUNContextobject.- Parameters:
sunctx – pointer to a valid
SUNContextobject,NULLupon successful return.
- Returns:
SUNErrCodeindicating 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
SUNContextobject.
- Returns:
the last
SUNErrCoderecorded.
-
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
SUNContextobject.
- Returns:
the last
SUNErrCoderecorded.
-
SUNErrCode SUNContext_PushErrHandler(SUNContext sunctx, SUNErrHandlerFn err_fn, void *err_user_data)
Pushes a new
SUNErrHandlerFnonto the error handler stack so that it is called when an error occurs inside of SUNDIALS.- Parameters:
sunctx – a valid
SUNContextobject.err_fn – a callback function of type
SUNErrHandlerFnto 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:
SUNErrCodeindicating success or failure.
-
SUNErrCode SUNContext_PopErrHandler(SUNContext sunctx)
Pops the last
SUNErrHandlerFnoff of the error handler stack.- Parameters:
sunctx – a valid
SUNContextobject.
- Returns:
SUNErrCodeindicating 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_PushErrHandlerotherwise errors will be ignored.- Parameters:
sunctx – a valid
SUNContextobject.
- Returns:
SUNErrCodeindicating success or failure.
-
SUNErrCode SUNContext_GetProfiler(SUNContext sunctx, SUNProfiler *profiler)
Gets the
SUNProfilerobject associated with theSUNContextobject.- Parameters:
sunctx – a valid
SUNContextobject.profiler – [in,out] a pointer to the
SUNProfilerobject associated with this context; will beNULLif profiling is not enabled.
- Returns:
SUNErrCodeindicating success or failure.
-
SUNErrCode SUNContext_SetProfiler(SUNContext sunctx, SUNProfiler profiler)
Sets the
SUNProfilerobject associated with theSUNContextobject.- Parameters:
sunctx – a valid
SUNContextobject.profiler – a
SUNProfilerobject to associate with this context; this is ignored if profiling is not enabled.
- Returns:
SUNErrCodeindicating success or failure.
-
SUNErrCode SUNContext_SetLogger(SUNContext sunctx, SUNLogger logger)
Sets the
SUNLoggerobject associated with theSUNContextobject.- Parameters:
sunctx – a valid
SUNContextobject.logger – a
SUNLoggerobject to associate with this context; this is ignored if logging is not enabled.
- Returns:
SUNErrCodeindicating success or failure.
Added in version 6.2.0.
-
SUNErrCode SUNContext_GetLogger(SUNContext sunctx, SUNLogger *logger)
Gets the
SUNLoggerobject associated with theSUNContextobject.- Parameters:
sunctx – a valid
SUNContextobject.logger – [in,out] a pointer to the
SUNLoggerobject associated with this context; will beNULLif logging is not enabled.
- Returns:
SUNErrCodeindicating success or failure.
Added in version 6.2.0.
1.3.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
SUNContextobject 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
SUNContextobject 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
SUNContextobject 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.3.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 get() override
{
return *sunctx_.get();
}
SUNContext get() 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