2.1. The SUNContext Type
New 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.
The SUNContext class/type is defined in the header file
sundials/sundials_context.h as
-
typedef struct _SUNContext *SUNContext
Users should create a SUNContext object prior to any other calls to
SUNDIALS library functions by calling:
-
int SUNContext_Create(void *comm, SUNContext *ctx)
Creates a
SUNContextobject associated with the thread of execution. The data of theSUNContextclass is private.- Arguments:
comm– a pointer to the MPI communicator or NULL if not using MPI.ctx– [in,out] upon successful exit, a pointer to the newly createdSUNContextobject.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
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(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:
-
int SUNContext_Free(SUNContext *ctx)
Frees the
SUNContextobject.- Arguments:
ctx– pointer to a validSUNContextobject,NULLupon successful return.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
Warning
When MPI is being used, the
SUNContext_Free()must be called prior toMPI_Finalize.
The SUNContext API further consists of the following functions:
-
int SUNContext_GetProfiler(SUNContext ctx, SUNProfiler *profiler)
Gets the
SUNProfilerobject associated with theSUNContextobject.- Arguments:
ctx– a validSUNContextobject.profiler– [in,out] a pointer to theSUNProfilerobject associated with this context; will beNULLif profiling is not enabled.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
-
int SUNContext_SetProfiler(SUNContext ctx, SUNProfiler profiler)
Sets the
SUNProfilerobject associated with theSUNContextobject.- Arguments:
ctx– a validSUNContextobject.profiler– aSUNProfilerobject to associate with this context; this is ignored if profiling is not enabled.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
-
int SUNContext_SetLogger(SUNContext ctx, SUNLogger logger)
Sets the
SUNLoggerobject associated with theSUNContextobject.- Arguments:
ctx– a validSUNContextobject.logger– aSUNLoggerobject to associate with this context; this is ignored if profiling is not enabled.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
New in version 6.2.0.
-
int SUNContext_GetLogger(SUNContext ctx, SUNLogger *logger)
Gets the
SUNLoggerobject associated with theSUNContextobject.- Arguments:
ctx– a validSUNContextobject.logger– [in,out] a pointer to theSUNLoggerobject associated with this context; will beNULLif profiling is not enabled.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
New in version 6.2.0.
2.1.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
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 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.
2.1.2. Convenience class for C++ Users
For C++ users, a class, sundials::Context, that follows RAII is provided:
namespace sundials
{
class Context
{
public:
explicit Context(void* comm = NULL)
{
sunctx_ = std::unique_ptr<SUNContext>(new 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;
operator SUNContext() { return *sunctx_.get(); }
~Context()
{
if (sunctx_)
SUNContext_Free(sunctx_.get());
}
private:
std::unique_ptr<SUNContext> sunctx_;
};
} // namespace sundials