-
Notifications
You must be signed in to change notification settings - Fork 156
Description
Issue description
To illustrate our questions, here is a MWE in the form of a simple one-equation ODE system:
where
The exact solution is a piece-wise parabola and we are mostly interested in the value at t=10 which is
How we solved this problem using CVode
See the example code in the "To replicate" section below.
-
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.
-
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
- This is somewhat expected since the root time coincides exactly with the asked time
t=10
. So we special casedCV_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++;
}
- 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
!
- 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
- Is it expected that
CVode()
does not returnCV_TOO_CLOSE
when init step size is set? - 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
-
Sundials version:
7.2.1
-
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