This sort of post hoc reasoning behind a decision made by a group of developers is a little dangerous. I think a better phrasing of the question might be, What are the advantages of NDSolveValue
over NDSolve
, given that NDSolveValue
returns only one of the solutions computed by NDSolve
? Encouraged by @Szabolcs and by the upvotes on my oversimplified comment, here goes....
I think the primary use of NDSolveValue
(singular, not plural NDSolveValues
) is to return a single function ifn
(or function expression ifn[x]
) that can be evaluated on values of the independent variable(s). @Carl Woll has already discussed some aspects of the convenience of this, but I would add that this use probably occurs many more times in practice than all other uses put together, because so many useful ODEs are of the form $y^{(n)} = F\big(x,y,y',\dots,y^{(n-1)}\big)$, usually just $y'=F(x,y)$ or $y''=F(x,y,y')$. NDSolve
will return a single solution to a valid IVP for such an ODE, and NDSolveValue
therefore will also return the only solution.
My comment suggested that Values@NDSolve[..]
would be equivalent to a "plural" form of NDSolveValue
, but that's not exactly right. NDSolveValue
is much more general in what it can return, except that its return value will be based on the first solution returned by NDSolve
, other solutions being ignored. AFAICT, the following two lines are equivalent:
NDSolveValue[<IVP>, expr, <INTERVAL>]
expr /. First@NDSolve[<IVP>, <VARIABLES>, <INTERVAL>]
It seems the expression expr
can be anything. If expr = 2
, then 2
is returned; if expr = Plot[Sin[t], {t, 0, 10}]
, then the plot is returned, warnings aside. As far as Values@NDSolve[..]
goes, the equivalence limited:
vnds = Values@ NDSolve[{x'[t]^2 == x[t]^2, x[0] == 1}, {x}, {t, 0, 1}];
ndsv = NDSolveValue[{x'[t]^2 == x[t]^2, x[0] == 1}, {x}, {t, 0, 1}];
First[vnds] === ndsv
(* True *)
vnds = Values@ NDSolve[{x'[t]^2 == x[t]^2, x[0] == 1}, x, {t, 0, 1}];
ndsv = NDSolveValue[{x'[t]^2 == x[t]^2, x[0] == 1}, x, {t, 0, 1}];
First[vnds] === ndsv
First@First[vnds] === ndsv
(*
False
True
*)
They are equivalent if expr = {x}
-- that is, if NDSolveValue
returns the value of the list {x}
, but not if NDSolveValue
returns just the function x
.
Instead of focusing on the issue of the lost solutions, one should rather think of the advantages of having some syntactic sugar equivalent to expr /. First@NDSolve[..]
. A couple favorites:
ListLinePlot@NDSolveValue[ivp, x, {t,..}] (* equiv. to Plot[x[t], {t,..}] *)
NSolveValue[{y'[x] == f[x], y[a] == 0}, (* equiv. to NIntegrate[f[x], {x, a, b}] *)
y[b], {x, a, b}]
Those are aside from the highly useful InterpolatingFunction
returned by NDSolveValue[ivp, x, {t, 0, 1}]
. I think almost everyone who uses ODEs a lot hates always having to remember to type First
in x[t] /. First[sol]
.
OTOH, if you are analyzing a solution, I find sol = NDSolve[ivp, x,..]
convenient for plugging into the ODE, invariants and so forth.
Workarounds:
(1)
If you want an NDSolveValues[]
or NDSolveValueList[]
, try this:
ClearAll[NDSolveValueList];
NDSolveValueList[sys_, expr_, indeps : {_, _, _} .., rest___] :=
Module[{sol, deps},
deps = Internal`ProcessEquations`FindDependentVariables[
Flatten@{sys}, {indeps}[[All, 1]]];
sol = NDSolve[sys, deps, indeps, rest];
(expr /. sol) /; FreeQ[sol, NDSolve]
];
I don't know much about Internal`ProcessEquations`FindDependentVariables
, so it might not work in all cases. Alternatively, for non-PDE systems, one might use the following, which uses documented functions:
ClearAll[NDSolveValueList];
NDSolveValueList[sys_, expr_, {t_, a_, b_}, opts___?OptionQ] :=
Module[{state, states},
states = NDSolve`ProcessEquations[sys, expr, {t, a, b}, opts];
Table[
state = states[[i]];
NDSolve`Iterate[state, {a, b}];
First@Values@NDSolve`ProcessSolutions@state,
{i, Length@states}] /; FreeQ[states, $Failed]
];
If there's a way to get the time-integration interval {a, b}
from state
, it does not seem to be documented. If someone finds or knows it, then this could probably be extended to PDEs with ease. The data is inside state
.
(2)
If you want simply to get a different solution from NDSolveValue
, there are two approaches. Both determine the solution by the highest derivative value. One uses the method "EquationSimplification" -> "Residual"
, and the other differentiates the ODE so that the derivative value becomes an initial condition.
NDSolveValue[
{x'[t]^2 == x[t]^2,
{x[0], x'[0]} == Values@
Solve[{x'[t]^2 == x[t]^2, x[0] == 1} /. t -> 0, {x'[0], x[0]}][[2]]},
x, {t, 0, 1},
Method -> {Automatic, "EquationSimplification" -> "Residual"}]
NDSolveValue[
{D[x'[t]^2 == x[t]^2, t],
{x[0], x'[0]} == Values@
Solve[{x'[t]^2 == x[t]^2, x[0] == 1} /. t -> 0, {x'[0], x[0]}][[2]]},
x, {t, 0, 1}]
NDSolveValue
spits outndsvb
warning, and I think the warning message is quite clear. And as mentioned by J.A in the answer below, this has been mentioned in the possible issues ofNDSolveValue
, at least from v11.2. $\endgroup$Values@NSolve[..]
....It's not clear to me what's hard to understand about the convenience of having a call that returns a functionf
that you can apply directly to inputf[0.2]
and so forth. You can't do that if a list of functions is returned, but have to useThrough[f[0.2]]
or some other workaround. $\endgroup$listx=Values@NSolve[..]
exactly returns what I would expect fromNDSolveValues
: A list of pure functions!!! The single solutions in listx can be addressed through listx[index][t] $\endgroup$