5
$\begingroup$

I'm trying to solve an ODE with two independent variables (a cannon firing from a cliff incorporating wind resistance dependent on velocity). I've tried the following for the x-component:

NDSolve[{x''[t, θ] == -0.2*x'[t, θ]/2.30, x'[0, θ] == 10.8*Cos[θ], x[0, θ] == 0},
  x[t, θ], t]

Assuming my algebra is correct, how do I properly phrase this input to give me x[t, θ]?

$\endgroup$
6
  • 1
    $\begingroup$ You probably want to rethink the title of your question: An ODE by definition only has one independent variable. In your example theta is just a parameter, not an independent variable... $\endgroup$ Feb 2, 2013 at 20:12
  • $\begingroup$ @AlbertRetey true, but the use of the primes indicates that only one of the variables is being used by the DE. $\endgroup$
    – rcollyer
    Feb 3, 2013 at 4:05
  • $\begingroup$ You need to use partial derivatives to tell Mathematica which variable is being varied, e.g. x'[t,θ] should be D[x[t,θ], x] and x''[t,θ] should be D[x[t,θ], {x, 2]. $\endgroup$
    – rcollyer
    Feb 3, 2013 at 4:08
  • $\begingroup$ @rcollyer: how does that change the fact that the title is a contradiction in terms? $\endgroup$ Feb 3, 2013 at 15:48
  • $\begingroup$ @AlbertRetey it doesn't. Only it is understandable why the OP decided on that terminology. $\endgroup$
    – rcollyer
    Feb 3, 2013 at 17:26

3 Answers 3

6
$\begingroup$

If :

sol = DSolve[{D[x[t, th], {t, 2}] == -0.2*D[x[t, th], t]/2.30, 
 Derivative[1, 0][x][0, th] == 10.8*Cos[th], x[0, th] == 0}, x[t, th], t];

then you can use the solution as :

Plot3D[x[t, th] /. sol, {t, 0, 10}, {th, -Pi, Pi}]

enter image description here

Some checks :

x[t, th] /. First[sol] /. t -> 0
(* 0. *)

Simplify[D[sol[[1, 1, 2]], t] /. t -> 0]
(* 10.8 Cos[th] *)

A more general and robust way to keep the solution for future use is to define :

Remove[sol]
sol[t_, th_] = DSolve[{D[x[t, th], {t, 2}] == -0.2*D[x[t, th], t]/2.30, 
 Derivative[1, 0][x][0, th] == 10.8*Cos[th], x[0, th] == 0}, x[t, th], t][[1, 1, 2]];

which you can now use as any other function. For instance :

Manipulate[Plot[sol[t, th], {t, 0, 10}], {th, 0, Pi/2}]
$\endgroup$
3
  • $\begingroup$ Thank you very much. I'm new to a lot of these terms and you've assisted me greatly. $\endgroup$
    – xaxXos
    Feb 3, 2013 at 0:24
  • $\begingroup$ I have one final question: How could this x[t,th] be plotted in 2D while varying th? I know it involves the Manipulate command but repeatedly get blank graphs. Here is one of my attempts: Manipulate[x[t, th] /. sol, {th, 0, Pi/2}] Where does my mistake lie? Thank-you @b.gatessucks $\endgroup$
    – xaxXos
    Feb 3, 2013 at 16:34
  • $\begingroup$ @user5708: I don't know how familiar you are with this site, but if you are happy with b.gatessucks answer you should accept it. If you expect more or different answers you could also wait with accepting. Ss the answer seems to do exactly what you want and is quite straightforward I woudn't expect more answers for this question, though... $\endgroup$ Feb 3, 2013 at 18:31
12
$\begingroup$

You have good answers already, but as I was nitpicking about your terminology I wanted to explain why. The strength of Mathematica compared to e.g. a pocket calculator is that it provides a powerful programming language and what you actually do when making good use of it is writing programs -- and for doing that it is in general very helpful to be precise about the concepts and terminology you are using. There are two reasons why it pays off to make a deliberate distinction between independent variables and parameters in this case:

It keeps you out of Trouble

I think your originial question arouse because you didn't know how to formulate a partial derivative with respect to one of two variables. b.gatessucks has shown how you can do that and then solve your system symbolically with DSolve. Had you made your code reflect the destinction between parameters and independent variables, that problem wouldn't even have bothered you as it is of course not actually necessary to write partial derivatives for an ODE.

It lets you do more with less Effort

When solving symbolically, the distinction between parameters and independent variables doesn't look like very important as the nature of the symbolic solution is that it is valid for all parameters. Still I would suggest to rewrite the symbolic solution in a form that reflects that we are solving an ODE for a set of parameters and the result is a (mathematical) function of only one independent variable:

Block[{a, b, th, x},
 symbolic[a_, b_, th_] = x /. First[DSolve[
     {(x'')[t] == -a x'[t], 
      x'[0] == b Cos[th], x[0] == 0}, x, t]]
 ]

For the moment the way I've defined the symbolic solution looks just like minor syntatic sugar. Here is how you can use it to create e.g. a plot of the result:

Plot[symbolic[0.2/2.3, 10.2, 0][t], {t, 0, 10}, 
 PlotRange -> {-100, 100}, Frame -> True]

There are subtleties to be explained: I define a function (computer sience terminology) which accepts three arguments (the parameters) and returns the abstraction of a mathematical function in the form of a Mathematica pure Function. As the symbolic solution is valid for all possible settings of the parameters we only need to find that solution once and can then just insert the parameters. This is why we can use Set (=) and don't need SetDelayed (:=): that way DSolve will only be called once at the time we define symbolic. To prevent possible problems with existing definitions for any of the symbols used as parameters and variables I'm making that definition within a Block.

For a majority of real world problems you will unfortunately not be able to find a symbolic solution and have to use numeric methods. That means that you will have to solve a different differential equation for every different set of parameters. Here is how you can do that:

numeric[a_?NumericQ, b_?NumericQ, th_?NumericQ] := Block[{x},
  x /. First[
    NDSolve[{x''[t] == -a*x'[t], x'[0] == b*Cos[th], 
      x[0] == 0}, {x}, {t, 0, 2}
     ]]
  ]

Most important is that I now have to use SetDelayed (:=) as now I have to make a new call to NDSolve for every new set of parameters. As NDSolve can only do its work when all parameters are given in numeric form it makes sense to only accept those arguments when they are numeric, thus the _?NumcericQ patterns. Again I use a Block to avoid possible problems with predefined variables, but this time that protection is not needed at the time numeric is defined but at the time when it is called, which is why the Block now is on the right hand side of the definition and not enclosing it. Again numeric is a (CS term.) function which returns a mathematical function but this time in the form of a Mathematica InterpolatingFunction. Fortunately we can use that almost everywhere in the same way as we could use the Function that was returned for the symbolic solution. This shows how you can make a plot for the numeric solution:

Plot[Evaluate[numeric[0.2/2.3, 10.2, 0][t]], {t, 0, 10}, 
 PlotRange -> {-100, 100}, Frame -> True]

Note that this is almost exactly the same as for the symbolic solution. Being able to access similar things in an uniform way is a great advantage when writing programs (== using Mathematica). I just added an Evaluate for performance reasons: that way the solution (in form of an InterpolatingFunction) is only calculated once and not afresh for every point for which Plot evaluates the solution. As the call to NDSolve is very fast in this case, it hardly makes a difference here but will make a huge difference for more complicated cases.

Finaly there is -- as Daniel Lichtblau has shown -- ParametricNDSolve, which provides a new way to solve parametrized differential equations in version 9. It can be used like this:

parametric = x /. First[ParametricNDSolve[
    {x''[t] == -a*x'[t], x'[0] == b*Cos[th], x[0] == 0}, {x}, {t, 0, 
     2}, {a, b, th}
    ]]

now the symbol parametric is set to a ParametricFunction object which does the same thing as the above definitions: given a set of parameters it will return a mathematical function (in the form of an InterpolatingFunction). As the ParametericFunction objects knows that it's always solving the same system just with different parameters it can do that in some cases in a more efficient way than a independent NDSolve. I have not yet much experience in how much difference that makes in practice, but it is a promising functionality. The nice thing is: with the above definitions, you can use the symbolic, the numeric and the parametric solutions in very much the same way, which makes it very easy to write something like this Manipulate which compares the three results (and shows that the numeric solutions are indeed very good approximations to the exact solution):

Manipulate[
 Plot[Evaluate[method[a, b, th][t]], {t, 0, 10}, 
  PlotRange -> {-100, 100}, Frame -> True, FrameLabel -> {"t", "x"}],
 {th, 0, Pi}, {{a, 0.2/2.3}, 0.01, 0.1}, {{b, 10.8}, 1, 20},
 {{method, symbolic}, {symbolic -> "symbolic", numeric -> "numeric", 
   parametric -> "parametric"}}
 ]

So making your code and syntax reflect the abstraction of what you are actually doing will make it easier to read, to understand and to use (as in the above Manipulate or when you need to exchange parts of your calculation at a later time).

$\endgroup$
1
  • $\begingroup$ I appreciate this very much @Albert Retey $\endgroup$
    – xaxXos
    Feb 6, 2013 at 2:00
10
$\begingroup$

You can use ParametricNDSolve in Mathematica 9. For example:

pp = ParametricNDSolve[{x''[t] == -0.2*x'[t]/2.30, 
    x'[0] == 10.8*Cos[θ], x[0] == 0}, {x, x'}, {t, 0, 
    2}, θ];

Manipulate[
 ParametricPlot[{x[θ][t], x'[θ][t]} /. pp, {t, 0, 2}]
 , {θ, 0, 2 Pi}]
$\endgroup$
1
  • $\begingroup$ Just tried that - awesome! $\endgroup$
    – xaxXos
    Feb 6, 2013 at 3:24

Your Answer

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

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