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 the SUNContext 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 created SUNContext 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 valid SUNContext 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 to MPI_Finalize.

The SUNContext API further consists of the following functions:

int SUNContext_GetProfiler(SUNContext ctx, SUNProfiler *profiler)

Gets the SUNProfiler object associated with the SUNContext object.

Arguments:
  • ctx – 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:
  • Will return < 0 if an error occurs, and zero otherwise.

int SUNContext_SetProfiler(SUNContext ctx, SUNProfiler profiler)

Sets the SUNProfiler object associated with the SUNContext object.

Arguments:
  • ctx – a valid SUNContext object.

  • profiler – a SUNProfiler 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 the SUNContext object.

Arguments:
  • ctx – a valid SUNContext object.

  • logger – a SUNLogger 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 the SUNContext object.

Arguments:
  • ctx – a valid SUNContext object.

  • logger – [in,out] a pointer to the SUNLogger object associated with this context; will be NULL 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