5
$\begingroup$

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.

$\endgroup$
4
$\begingroup$

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}}

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

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.

enter image description here

$\endgroup$
  • 1
    $\begingroup$ Elegant solution, didn't think of that. Thank you. $\endgroup$ – Alex Oct 21 '15 at 15:56
  • 1
    $\begingroup$ 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"]] $\endgroup$ – george2079 Oct 21 '15 at 16:05
2
$\begingroup$

I would rather do it in vector form:

i = 0;
Needs["DifferentialEquations`InterpolatingFunctionAnatomy`"];
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)

Mathematica graphics

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.