2
$\begingroup$

Here I define the equations:

n = 5;
m = 1/n*IdentityMatrix[n];
k = n*SparseArray[{{i_, j_} /; Abs[i - j] == 1 -> -1, {1, 1} -> 
       1, {i_, i_} /; i != n -> 2, {n, n} -> 1}, {n, n}] // Normal;
X = Table[Indexed[x, i], {i, n}];
V = Table[Indexed[v, i], {i, n}];

eqs0 := Thread[m.D[Through[X[t]], {t, 2}] + k.Through[X[t]] == 0];
ics = Thread[Through[X[0]] == -1]~
   Join~(Thread[D[Through[X[t]], {t, 1}] == 1] /. t -> 0);

Then solving with NDSolve works fine:

sol = NDSolveValue[
  eqs0~Join~ics~
   Join~{WhenEvent[Indexed[x, {5}][t] == 1, 
     Indexed[x, {5}]'[t] -> -Indexed[x, {5}]'[t]]}, X, {t, 0, 12.34}]

However, if I replace {5} with {n}, or Evaluate@{n}, I get the error

"The function value x5[0.`] == 1 is not True or False when the arguments are"

The issue is that I want the WhenEvent to depend on n. How can I achieve that?

$\endgroup$
3
  • $\begingroup$ I get a NDSolveValue::deqn error instead. What is the message name of the error you get? $\endgroup$
    – Michael E2
    Sep 14, 2017 at 0:14
  • $\begingroup$ @MichaelE2 NDSolveValue::nbnum1 $\endgroup$
    – anderstood
    Sep 14, 2017 at 0:17
  • 1
    $\begingroup$ How about: sol = NDSolveValue[eqs0~Join~ics~Join~{WhenEvent @@ {Indexed[x, {n}][t] == 1, Indexed[x, {n}]'[t] -> -Indexed[x, {n}]'[t]}}, X, {t, 0, 12.34}]? $\endgroup$
    – Michael E2
    Sep 14, 2017 at 0:19

1 Answer 1

2
$\begingroup$

Since WhenEvent is HoldAll, the trick is to get all the instances of n inside WhenEvent to be evaluated. I believe this is necessary because variables like Indexed[x, {5}] are replaced by values with ReplaceAll or something like it. That means the code that matches Indexed[x, {5}] literally will be replaced by its current value, whereas Indexed[x, {n}] in the held code Indexed[x, {n}][t] == 1 does not match Indexed[x, {5}] literally; however, when Indexed[x {n}] bubbles up to a level where it is evaluated, such as in the message, it appears as Indexed[x, {5}].

Here are a couple of fixes. The second lets only n be evaluated, and probably should be preferred.

sol = NDSolveValue[{eqs0, ics,
   WhenEvent @@ {Indexed[x, {n}][t] == 1, 
     Indexed[x, {n}]'[t] -> -Indexed[x, {n}]'[t]}}, X, {t, 0, 12.34}]

sol = NDSolveValue[{eqs0, ics, 
   With[{n = n}, 
    WhenEvent[Indexed[x, {n}][t] == 1, 
     Indexed[x, {n}]'[t] -> -Indexed[x, {n}]'[t]]]}, X, {t, 0, 12.34}]
$\endgroup$
3
  • 1
    $\begingroup$ Would you know what is the advantage of making WhenEvent HoldAll? $\endgroup$
    – anderstood
    Sep 14, 2017 at 0:51
  • 1
    $\begingroup$ @anderstood First, you certainly want it to be at least HoldRest, since otherwise Sow in the second argument would be evaluated only when NDSolve is called and never for an event. Second, the last two examples in the docs under Scope > Events (with AbsoluteTime[] and stop respectively) would not work if the first argument is not held. $\endgroup$
    – Michael E2
    Sep 14, 2017 at 1:16
  • 1
    $\begingroup$ Another possibility is code that would evaluate differently if the variables were symbolic vs. replaced by numeric values. (No good examples at hand, but functions such as MemberQ or PositiveDefiniteMatrixQ come to mind.) I'm afraid I've thought of no real compelling examples, but OTOH, if it were not HoldAll, I'd have to use WhenEvent[evt[t, x[t],...], action], where evt[..] is a NumericQ-protected function for many events. That seems equally undesirable. $\endgroup$
    – Michael E2
    Sep 14, 2017 at 1:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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