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
SUNContext
object associated with the thread of execution. The data of theSUNContext
class 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 createdSUNContext
object.
- 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
SUNContext
object.- Arguments:
ctx
– pointer to a validSUNContext
object,NULL
upon 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
SUNProfiler
object associated with theSUNContext
object.- Arguments:
ctx
– a validSUNContext
object.profiler
– [in,out] a pointer to theSUNProfiler
object associated with this context; will beNULL
if profiling is not enabled.
- Returns:
Will return < 0 if an error occurs, and zero otherwise.
-
int SUNContext_SetProfiler(SUNContext ctx, SUNProfiler profiler)
Sets the
SUNProfiler
object associated with theSUNContext
object.- Arguments:
ctx
– a validSUNContext
object.profiler
– aSUNProfiler
object 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
SUNLogger
object associated with theSUNContext
object.- Arguments:
ctx
– a validSUNContext
object.logger
– aSUNLogger
object 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
SUNLogger
object associated with theSUNContext
object.- Arguments:
ctx
– a validSUNContext
object.logger
– [in,out] a pointer to theSUNLogger
object associated with this context; will beNULL
if 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
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.
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