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