2.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.

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:

SUNErrCode SUNContext_Create(SUNComm comm, SUNContext *sunctx)

Creates a SUNContext object associated with the thread of execution. The data of the SUNContext 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 to MPI_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:
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:
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:
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:
Returns:

SUNErrCode indicating success or failure.

SUNErrCode SUNContext_GetProfiler(SUNContext sunctx, SUNProfiler *profiler)

Gets the SUNProfiler object associated with the SUNContext object.

Parameters:
  • sunctx – a valid SUNContext object.

  • profiler – [in,out] a pointer to the SUNProfiler object associated with this context; will be NULL if profiling is not enabled.

Returns:

SUNErrCode indicating success or failure.

SUNErrCode SUNContext_SetProfiler(SUNContext sunctx, SUNProfiler profiler)

Sets the SUNProfiler object associated with the SUNContext 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 the SUNContext 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 the SUNContext object.

Parameters:
  • sunctx – a valid SUNContext object.

  • logger – [in,out] a pointer to the SUNLogger object associated with this context; will be NULL if logging is not enabled.

Returns:

SUNErrCode indicating success or failure.

Added in version 6.2.0.

2.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.

2.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