Skip to content

CVode does not return CV_TOO_CLOSE when initial step size is set #722

@eltix

Description

@eltix

Issue description

To illustrate our questions, here is a MWE in the form of a simple one-equation ODE system:

$$\begin{aligned} & \dot y = \mathrm{mod}(t, 1)\\\ & y(t=0) = 0 \\\ & 0 \le t \le 10 \end{aligned}$$

where $\mathrm{mod}(t, 1)$ stands for the fractional part of $t$.
The exact solution is a piece-wise parabola and we are mostly interested in the value at t=10 which is $y(t=10)=5$.

How we solved this problem using CVode

See the example code in the "To replicate" section below.

  1. By default, CVode may give incorrect results because the ODE RHS is not continuous and therefore the solver may "step over" the discontinuity should it take a large enough step.

  2. This is expected so we added a root function which triggers at each discontinuity of the RHS, that is for $t = k, k \in \mathbb N$:

static int root_fn(sunrealtype t, N_Vector y, sunrealtype *gout, void *user_data) {
  sunrealtype y1, b;

  y1 = Ith(y, 1);
  b = SUN_RCONST(1);

  // a jig-saw function which has roots at t = k, k being any integer
  *gout = fabs( fmod(t-b/2, 2*b) - b) - b/2;
  return 0;
}

But now CVode() returns CV_TOO_CLOSE:

[ERROR][rank 0][/build/sundials-7.2.1/src/cvode/cvode.c:3710][cvHandleFailure] tout too close to t0 to start integration.
At t_out = 1.00e+01 t = 1.000000e+01 y1 = 5.0e+00 tcur = 1.0e+01 step = 0.0e+00 order = 0 err1 = -8.8e-07 
retval = -27, return 1
  1. This is somewhat expected since the root time coincides exactly with the asked time t=10. So we special cased CV_TOO_CLOSE. At this point, if you're trying to reproduce the issue, you should uncomment this block at lines 153-159:
    else if (retval == CV_TOO_CLOSE) {
      /* This happens if tout is too close to t. In such a case, no solving was required; just continue as if solving succeeded. 
      However, if the init step is set using CVodeSetInitStep, CVode DOES NOT RETURN CV_TOO_CLOSE, see below
      */
      printf("retval == CV_TOO_CLOSE\n");
      iout++;
    }
  1. Up until now, the init step size was not set. Let's set it (uncomment lines 109-110):
  retval = CVodeSetInitStep(cvode_mem, INITSTEP);
  if (check_retval(&retval, "CVodeSetInitStep", 1)) { return (1); }

Now we have an infinite loop because CVode() no longer returns CV_TOO_CLOSE!

  1. This is where things get interesting and we are seeking guidance. To prevent the infinite loop, we wrote an additional safeguard (uncomment lines 160-167):
    else if (retval == CV_ROOT_RETURN && t == tout) {
      /* See the above
         if the init step is set using CVodeSetInitStep, CVode DOES NOT RETURN CV_TOO_CLOSE when tout is too close to t
         In such a case, pretend that the root didn't happen, lest we keep handling it forever. 
      */
      printf("t == tout && retval == CV_ROOT_RETURN\n");
      iout++;
    }

This seems to work but the results are actually incorrect since we have y(t=10) = 0.5 instead of 5:

At t_out = 1.00e+01 t = 1.000000e+01 y1 = 5.0e-01 tcur = 1.0e+01 step = 1.0e+00 order = 1 err1 = 0.0e+00

The questions

  1. Is it expected that CVode() does not return CV_TOO_CLOSE when init step size is set?
  2. In general, how should we handle cases where the root finding function triggers on a time that coincides with a requested solution time?

To replicate

  1. Sundials version: 7.2.1

  2. example.zip contains an example.c source file which you can compile with

gcc example.c -lsundials_cvode -lsundials_core -lm

and then run with

./a.out

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions