I loop over some systems of DAEs with NDSolve; some solutions are periodic, some are not. When integrating with NDSolve, I integrate on {t,-1000,1000} starting from t=0 because I don't want to compute only one part of the non-periodic solutions. The problem is that when orbits are periodic, it computes the solution on several periods. To avoid this, I used EventLocator with the following testing function:
isPeriodic[vals_, ic_] := (Norm[vals - ic] < .02) && (t < -1 || t > 1)
The 0.02 instead of 0 is due to numerical inaccuracies which makes the solution not perfectly periodic, but "almost". The condition t < -1 || t > 1 is used to prevent NDSolve from stopping immediately after starting ($f(0.000001)$ and $f(0)$ would trigger the periodicity condition since $\|f(0.000001)-f(0)\|$ is very likely to be lower than $0.02$).
This works pretty well, except that I it returns the solution over two periods: $f(t)==f(0)$ is checked both for positive and negative t.
I could of course split the interval in two and keep only one part, but for some reasons I'd prefer to avoid that; additionally, it would avoid computing the periodic solution on two periods.
Full code, which may seem sligthly complex but is not really:
(* defines variables and system of DAE *)
vars = Array[s, 2];
varst = Through[vars[t]];
ic = {56.30672012303853`, 1.9416110387254666`};
sysDAE0 = {{0.2763932022500211` Sin[
0.8090169943749473` (-s[1][t] + s[2][t])] Sin[
0.3090169943749473` (s[1][t] + s[2][t])] +
0.7236067977499788` Sin[
0.3090169943749473` (-s[1][t] + s[2][t])] Sin[
0.8090169943749473` (s[1][t] + s[2][t])]} == {0},
Derivative[1][s[1]][t]^2 + Derivative[1][s[2]][t]^2 ==
1.`, {s[1][0], s[2][0]} ==
ic, {Derivative[1][s[1]][0],
Derivative[1][s[2]][0]} == {-0.8903443552034469`,
0.45528774325404187`}};
(* test of periodicity *)
isPeriodic[vals_,
ci_] := (Norm[vals - ci] < .02) && (t < -1 || t > 1)
(* solves the system *)
{solDAE} =
NDSolve[sysDAE0, vars, {t, -1000, 1000},
Method -> {"EventLocator", "Event" -> isPeriodic[varst, ic],
"EventAction" :> Throw[Null, "StopIntegration"]}]
sol[t_] := Through[(vars /. solDAE)[t]];
{tmin, tmax} = Flatten@solDAE[[1]][[2, 1]];
And the output, which is a closed curve (periodicity) plotted twice:
ParametricPlot[sol[t], {t, tmin, tmax}]
Question How to use events to compute only one period of the solution? One possibility would be to stop integration in both positive and negative t when the first periodicity condition is found, but "StopIntegration" does not do that (luckily).
Another possibility is to introduce a boolean boolPeriodic, turning it to True when the first periodic event is detected, and adding this boolean as an event to kill immediately the integration in the other direction (hence, avoiding computing a second time the period). But it does not help: it seems the EventLocator method is run in parallel for positive t and negative t. This is confirmed by adding a Print in the EventAction:
boolPeriodic = False; {solDAE} =
NDSolve[sysDAE0, vars, {t, -1000, 1000},
Method -> {"EventLocator",
"Event" -> isPeriodic[varst, ic] || boolPeriodic,
"EventAction" :> Throw[boolPeriodic = True;Print["periodic"], "StopIntegration"]}]
sol[t_] := Through[(vars /. solDAE)[t]];
(* prints TWO "periodic" *)




WhenEvent[]superseded `"EventLocator" in V9 -- what version are you using? – Michael E2 Oct 30 '16 at 23:36WhenEvent[]but did not manage to make it work. I'm going to add the code I tried (but I don't think it will change something). – anderstood Oct 30 '16 at 23:37{t, 0, 1000}(or{t, 0 Infinity}), won't that give you what you want? – Michael E2 Oct 30 '16 at 23:45t == 0in this case). So the forward integration0 < t < 1000captures one period and the backward integration0 > t > -1000get another period. – Michael E2 Oct 30 '16 at 23:51{t, 0, 1000}. – anderstood Oct 31 '16 at 00:22t, say,{t, -0.1, 0}, with oneNDSolvecall, and use the values ofvarsatt == -0.1to construct the stopping criterion for a secondNDSolvecall over{t, -1000, 1000}. I don't think the initial condition can be an "event." – Michael E2 Oct 31 '16 at 10:33