# If statement within WhenEvent

I'm trying to integrate an equation with a periodic solution using NDsolve. I want to stop the integration after the derivative of my solution has become zero for the $n$-th time (in my example code, $n=5$). For this purpose I included a "counter" variable $i$ in WhenEvent. In principle everything works fine, except that here "StopIntegration" is not recognized. I guess it has to do something with the fact that "StopIntegration" is wrapped within an If statement. However, in principle "StopIntegration" should be read by Mathematica (as Print["Integration stopped at t=", tend] is) and NDSolve should stop? Below is a simple minimal working example of my Problem.

Module[{i = 0},
First@NDSolve[{D[x[t], t] == 2 π y[t],
D[y[t], t] == -2 π x[t], x[0] == 0, y[0] == 1,
WhenEvent[y[t] == 0,
If[i >= 4, {tend = t, "StopIntegration",
Print["Integration stopped at t=", tend]}, i += 1]],
WhenEvent[t == 10, tend = t]}, {x, y}, {t, 0, 10},
Method -> "LSODA"]]


Any help would be highly appreciated.

The string "StopIntegration" needs to be the result of the If evaluation (You are returning it as part of a list. This seems to work:

sol=Module[{i = 0},
First@NDSolve[{D[x[t], t] == 2 \[Pi] y[t],
D[y[t], t] == -2 \[Pi] x[t], x[0] == 0, y[0] == 1,
WhenEvent[y[t] == 0,
If[i >= 4, tend = t; Print["Integration stopped at t=", tend];
"StopIntegration", i += 1]], WhenEvent[t == 10, tend = t]},
{x, y}, {t, 0, 10}, Method -> "LSODA"]]


Aside, The terminating time is captured in the "Domain" of the solution, so you don't need to capture tend like that.

(x /. sol)["Domain"]


{{0., 2.25}}

• Thanks, that helped me lot. Interestingly, without the If function, feeding a list to WhenEvent does work. Strange. – Alex Oct 21 '15 at 15:55
• That is odd - I was puzzled why @march's solutuion works. – george2079 Oct 21 '15 at 15:57
• Reviewing the docs, The WhenEvent action can be a list. This is a possible bug that If[ cond , list ] fails. – george2079 Oct 21 '15 at 16:47

I believe this will also work. Use i as a DiscreteVariable:

sols = First@NDSolve[
{D[x[t], t] == 2 π y[t], D[y[t], t] == -2 π x[t]
, x[0] == 0, y[0] == 1
, i[0] == 0
, WhenEvent[y[t] == 0, i[t] -> i[t] + 1]
, WhenEvent[i[t] == 4, {"StopIntegration", Print[tend = t]}]
, WhenEvent[t == 10, tend = t]
}
, {x, y, i}
, {t, 0, 10}
, Method -> "LSODA"
, DiscreteVariables -> {i}
]
Plot[{i[t], y[t]} /. % // Evaluate, {t, 0, tend}]


### Update

george2079 points out that the WhenEvents can be rolled into one via

WhenEvent[y[t] == 0, If[i[t] < 4, i[t] -> i[t] + 1, Print[t]; "StopIntegration"]]


which is cleaner. Then, as he pointed out in his solution, one can use "Domain" to extract tend.

• Elegant solution, didn't think of that. Thank you. – Alex Oct 21 '15 at 15:56
• This is a cleaner method of counting.. You can roll that into a single WhenEvent: WhenEvent[y[t] == 0, If[i[t] < 4, i[t] -> i[t] + 1, Print[t]; "StopIntegration"]] – george2079 Oct 21 '15 at 16:05

I would rather do it in vector form:

i = 0;
Needs["DifferentialEquationsInterpolatingFunctionAnatomy"];
sols = NDSolveValue[{s'[t] == 2 Pi RotationMatrix[-Pi/2].s[t], s@0 == {0, 1},
WhenEvent[Last@s[t] == 0, If[++i > 4, "StopIntegration"]]},
s, {t, 0, 10}]

Plot[sols[t], {t, 0, #}, PlotLabel -> "Integrated up to " <> ToString@#] &@
(InterpolatingFunctionDomain@ sols // Flatten // Last)