I have to numerically integrate an equation system and monitor the accumulating datapoints. For example, I fit a line to a subsample of the points and terminate via "StopIntegration" if fitted line has ~0 slope. However, when I define a regular sampling time for WhenEvent (Mod[t, 1]), the collected log values have huge errors.
ode = {
x'[t] == -.2 x[t]^2 + 2 y[t],
y'[t] == x[t] + .1 x[t]^2 - 1.5 y[t],
x[0] == y[0] == 1,
WhenEvent[Mod[t, 1] == 0,
If[140 < t < 200, AppendTo[events, {t, Log@x@t}]]]
};
events = steps = {};
if = NDSolveValue[ode, {x, y}, {t, 0, 200}, StepMonitor :> AppendTo[steps, {t, Log@x@t}]];
LogPlot[Through@if@t, {t, 0, 200}, Epilog -> {
{Blue, AbsolutePointSize@6, Point@steps},
{Red, AbsolutePointSize@3, Point@events}
}, ImageSize -> 500]

I can see two ways to overcome this. 1) Set a method for NDSolve with uniform known stepsize and make the WhenEvent test at the same times. For certain reasons I want to avoid this. For one, it's nice to rely on the automatic adaptive stepsize algorithm. 2) Force WhenEvent to only evaluate when an internal step is taken by the integrator without modifying the step size manually. I failed to achieve this reliably. How to do this?
As you can see, it's perfectly unnecessary to increase accuracy/precision, as at steps the solution values are correct and I don't need to have extra values in between them. Note, that I cannot use StepMonitor solely, as I have to stop integration when a condition is met (based on the collected points).
Details
The problem is in the way Mathematica calculates solution values but not via the actual integrator method: when a value is calculated, WhenEvent interface does not know where the next point will be, so it has to extrapolate. It just does it in a horrible way. Somewhere in the process the actual derivative is lost, or even worse, a constant derivative is assumed, resulting in wild errors. See the following, even simpler example:
Block[{events = {}, steps = {}, if, x, t},
if = NDSolveValue[{x'[t] == -1/10 x[t], x[0] == 1,
WhenEvent[Mod[t, #] == 0., AppendTo[events, {t, x[t]}]]},
x, {t, 0, 100},
StepMonitor :> {AppendTo[steps, t]},
Method -> "ExplicitRungeKutta"(*,WorkingPrecision -> 50*)];
Plot[if@t, {t, 0, 100},
Epilog -> {Green, Point@events},
PlotRange -> {All, {1, -1}}, GridLines -> {steps, {0}},
PlotLabel -> Row@{"d = ", #}]
] & /@ {10, 1, 1/10, 1/100}

Vertical gridlines indicate internal steps, green points are the detected events. If you uncomment the option WorkingPrecision -> 50, the result is what one would expect - at the cost of at least double time required and I had to convert all reals to exact numbers.
Update
According to TechSupport, there is no way at the moment to evaluate a WhenEvent test only when the integrator takes a step. Maybe in a future version.






WhenEvent[Abs[x'[t]] < 10^-4, "StopIntegration"]? – Chris K Mar 15 '16 at 00:44WhenEventbecomes true, it won't check that test any more, even if you reset the flag. – István Zachar Mar 16 '16 at 09:22