Update: Clarified when an event may depend on the highest-order derivative. Thanks to @Chris K for pointing out an exception to the original characterization.
I think the practical answer to the main question is to test whether the condition is true initially "initially", that is, before one calls NDSolve[].
However, I would like to respond to the observation in the OP of the effect of changing the order in And[] of the event. Maybe there's a better place to post this way to inspect an event as it is constructed by NDSolve[], but I didn't find one. I know the question of changing the order has come up before, though.
An event of the form evt && cond is parse in a special way. The first argument is processed as an event, that is, equations and inequalities are converted to a numeric function whose sign change in a step from t == t1 to t == t2 indicates an event has occurred. The second argument represent a boolean condition: if true at an event, then the event action is taken. It appears there is a restriction on the condition. It may depend only the highest-order derivative if either the derivative appears in evt or the option setting Method -> {"EquationSimplification" -> "Residual"} is used.
(Normally in an explicit ODE $Y' = F(t, Y)$, the derivatives $Y'$ are not considered "state variables."
In an implicit ODE $F(t,Y,Y')=0$, which is what "EquationSimplification" -> "Residual" enforces, the derivatives $Y'$ treated as state variables. At least that's how I keep the difference in mind, plus maybe it's more efficient to have events depend on fewer variables.)
If the form of the event is not of the form evt && cond, or if evt is not of one of the forms given in the table of example event forms in the documentation for WhenEvent[], then the whole event is treated as a boolean function and an event is indicated by its changing from False to True (only).
{state1} = NDSolve`ProcessEquations[{x'[t] == 0, x[0] == 1,
WhenEvent[Abs[x'[t]] == 10^-3 && t >= 0 , Echo@{"FOUND at", t};
"StopIntegration"]},
x, {t, 0, 20}];
{state2} =
NDSolve`ProcessEquations[{x'[t] == 0, x[0] == 1,
WhenEvent[t >= 0 && Abs[x'[t]] <= 10^-3, Echo@{"FOUND at", t};
"StopIntegration"]}, x, {t, 0, 20}];
{state3} =
NDSolve`ProcessEquations[{x'[t] == 0, x[0] == 1,
WhenEvent[t >= 0 && Abs[x'[t]] <= 10^-3, Echo@{"FOUND at", t};
"StopIntegration"]}, x, {t, 0, 20},
Method -> {"EquationSimplification" -> "Residual"}];
The state data object has two lists of NDSolve`EventData[], one for the backward integration direction and one for the forward one. Initially they are the same, so we'll examine just one for each state.
state1["Events"]
SameQ @@ %
evt1 = state1["Events"][[2, 1]];
evt2 = state2["Events"][[2, 1]];
evt3 = state3["Events"][[2, 1]];
The following shows the structure of the NDSolve`EventData[] for each ODE, at least as much as I have been able to discover. Note the difference in the condition in the 2nd and 3rd ODEs. Depending on the method of equation simplification, in the condition, the x'[t] is replaced by a numerical function argument or left as constant parameter of the numerical function. Note also that the evt, namely, t >= 0, has be simplified to True (presumably because the interval of integration is 0 <= t <= 20}).
(* formatters *)
headers = {"{?,?,?,?,Priority,var}", "symbolic event", "evt (NF)",
"condition (NF)", "?", "DetectionMethod", "?", "LocationMethod",
"?", "?"};
gridform =
Grid[Transpose@{headers, #}, Alignment -> {{Center, Left}}(*,
Dividers->All*),
Background -> {None, {{GrayLevel[0.95], GrayLevel[0.99]}}}] &;
List @@ evt1 // gridform

List @@ evt2 // gridform

List @@ evt3 // gridform

Thanks to @xzczd for these references to related Q&A:
WhenEvent[…&&…, …]is confusing. Somewhat related: https://mathematica.stackexchange.com/questions/211090/whenevent-fails-in-ndsolve/211091#comment541212_211091 I think thenbnum1is a bug BTW. – xzczd Aug 19 '22 at 12:30NDSolve? – Michael E2 Aug 19 '22 at 13:37NDSolveto do it initially, and was surprised to see it otherwise. – István Zachar Aug 19 '22 at 13:44event, sinceAbs[x'[t]never changes. You might need to just test it manually before you startNDSolve. – Chris K Aug 19 '22 at 13:46NDSolveValue[{x'[t] == -0.9 x[t], x[-$MachineEpsilon] == 1, WhenEvent[Abs[x'[t]] <= 10^-3, Echo@{"FOUND at", t, " x[t]=", x[t], " x'[t]=", x'[t]}; "StopIntegration"]}, x, {t, 0, 20}]reportsx'[t]=-0.001, whereasNDSolveValue[{x'[t] == 0, x[-$MachineEpsilon] == 1, WhenEvent[t == 0, Echo@{"FOUND at", t, " x[t]=", x[t], " x'[t]=", x'[t]}; "StopIntegration"]}, x, {t, 0, 20}]reportsx'[t]=x'[0.]. So the derivative doesn't seem to be properly set when the event is triggered bytcompared to when it is triggered byx'[t]. The bug? – Chris K Aug 19 '22 at 14:02Method -> {"EquationSimplification" -> "Residual"}and you get the value ofx'[t]. Mentioned in my answer below. – Michael E2 Aug 19 '22 at 15:40x'[t]does evaluate in my first example! – Chris K Aug 19 '22 at 16:02Abs[x'[t]] <= 10^-3in your first example toAbs[x[t]] <= 10^-3, thenx'[t]is not evaluated. Perhaps it's whetherx'[t]appears in the event (or more preciselyevtin my answer) that determines whether the value ofx'[t]will be substituted or not. I'll look into it later. – Michael E2 Aug 19 '22 at 16:11