9
$\begingroup$

I would like to solve the following system of partial differential equations with Mathematica: $$x\frac{\partial f(x,y)}{\partial x}=-f(x,y)\\y\frac{\partial f(x,y)}{\partial y}=-f(x,y)\\$$ The solution to this system is $$f(x,y)=\frac{\mathrm{const.}}{xy}$$ if I am not mistaken. However, if I try to solve

DSolve[{x*D[f[x, y], x] == -f[x, y], y*D[f[x, y], y] == -f[x, y]}, 
 f[x, y], {x, y}]

with Mathematica, it does not complain but doesn't give a solution either, it only prints the input again. Can anyone help me solve this?

$\endgroup$
1
  • 2
    $\begingroup$ It appears that DSolve does not recognize it as a type of PDE it can solve. The PDE can be put in the form $\nabla f = {\bf B}(f, x, y,\dots)$, which has a solution only if $\text{curl}({\bf B})$ is zero, I believe. Since in general that's a rare event, perhaps it does not try. It can solve systems in with $\bf B$ is a function of the independent variables only. $\endgroup$
    – Michael E2
    Commented May 13, 2017 at 2:39

2 Answers 2

10
+100
$\begingroup$

A bit too long for a comment: I do not know why the your command does not return the result, but you can obtain the solution by solving the two equations successively:

 sol1 = DSolve[{x*D[f[x, y], x] + f[x, y] == 0}, f, {x, y}]
 (* {{f -> Function[{x, y}, C[1][y]/x]}} *)

 sol2 = DSolve[y*D[f[x, y], y] == -f[x, y] /. sol1, C[1], {y}]
 (* {{C[1] -> Function[{y}, C[2]/y]}} *)

 f[x, y] /. sol1 /. sol2 // First // First
 (* C[2]/(x y) *)

which yields the result you provided.

$\endgroup$
4
  • 4
    $\begingroup$ alternatively one may use DSolve[{x D[f[x, y], x] == y D[f[x, y], y]}, f[x, y], {x, y}], which tells you that f is a function of x*y only, and then DSolve[x D[f[x], x] == -f[x], f[x], x] to verify your result. So in principle, Mathematica knows the answer $\endgroup$
    – user46676
    Commented Apr 3, 2017 at 14:34
  • 1
    $\begingroup$ Thank you very much @anderstood. Do you think this is a shortcoming of Mathematica or is there something missing in my command? $\endgroup$
    – knl
    Commented Apr 4, 2017 at 14:51
  • 2
    $\begingroup$ @knl I see nothing wrong in your command. Thank you for the accept, however I think you should keep it for someone who actually answers your question by explaining why your command is not working. $\endgroup$
    – anderstood
    Commented Apr 4, 2017 at 15:02
  • 2
    $\begingroup$ +1. Just a caveat: This is the method for finding potential functions presented in most calculus books, and in general one needs to check that sol2 is free of x -- that is, that it is a function of y only. For instance, for the PDE {x*D[f[x, y], x] == -f[x, y], y*D[f[x, y], y] == -x f[x, y]}, the procedure gives an answer that is not a solution. $\endgroup$
    – Michael E2
    Commented May 13, 2017 at 14:13
10
$\begingroup$

Introduction

1. After looking at it, I think DSolve (as of V11.1.1) is not set up to solve such an equation. It can solve $\nabla f(x,y,z) = {\bf B}(x,y,z)$, where $\bf B$ is a curl-free vector field dependent only on the independent variables $x,\,y,\,z$. DSolve will only do this over $\bf R^2$ or $\bf R^3$.

2. One can extend the basic idea in @anderstood's answer to a procedure that will solve PDEs of the form $A.\nabla u({\bf x}) = {\bf B}(u, {\bf x})$, where $A=A(u,{\bf x})$ is a matrix. Of course, the equations have to solvable in the Mathematica sense.

What DSolve does

DSolve seems to base its parsing on the number of equations 2 and the number of dependent variables 1. It then tests for two kinds of equations, a Cauchy problem in which the second equation is an initial condition and a gradient/potential function problem. It rejects the first, of course because both are differential equations. For the second, it checks that the non-gradient terms are free of f, and since they contain f[x, y], DSolve rejects this approach and fails.

A workaround

One can use successive integrations as in @anderstood's answer. One needs to check that after each integration, the dependence on the variable integrated has vanaished; otherwise, the curl is not zero and the system has no solution.

ClearAll[dsolvePotentialPDE];
dsolvePotentialPDE::irrot = "Vector field is not irrotational.";
dsolvePotentialPDE::dsolve = 
  "DSolve could not integrate component ``.";
integrate1D[{sols_, df_, {u_}, varsTBD_, varsDone_}] :=
  Module[{sol1}
   , sol1 = DSolve[Fold[ReplaceAll, df[[1]], sols], u, varsTBD]
   ; If[! FreeQ[sol1, Alternatives @@ varsDone]
    , Message[dsolvePotentialPDE::irrot]
    ; Throw[$Failed]]
   ; If[ListQ[sol1], sol1 = First[sol1]
    , Message[dsolvePotentialPDE::dsolve, Unevaluated@sol1]
    ; Throw[$Failed]]
   ; {Append[sols, sol1]
    , Rest@df
    , Cases[{u @@ varsTBD /. sol1}, C[n_][__] :> C[n], Infinity, 1]
    , Rest@varsTBD
    , Append[varsDone, First@varsTBD]}
   ];
dsolvePotentialPDE[pde : {__Equal} | _Equal, f0_, vars_List] :=
  Module[{f, grad, sol, eq2}
   , f = Internal`ProcessEquations`SimplifyDependent[
      {f0}, vars, dsolvePotentialPDE, True
      ][[1, 1]]
   ; grad = Solve[pde, D[f @@ vars, {vars}]]
   ; If[Length@grad == 1 && Length /@ grad === {Length@vars}
    , grad = Equal @@@ First@grad
    ; sol = 
     Catch[First@
       Nest[integrate1D, {{}, grad, {f}, vars, {}}, Length@vars]]
    , sol = $Failed(*wrong kind of PDE*)
    ];
   DSolve`DSolveToPureFunction[
     f @@ vars -> Fold[ReplaceAll, f @@ vars, sol], {f0}
     ] /; FreeQ[sol, $Failed]
   ];

Examples. OP's:

pde = {x*D[f[x, y], x] == -f[x, y], y*D[f[x, y], y] == -f[x, y]};

dsolvePotentialPDE[pde, f, {x, y}]
(*  f -> Function[{x, y}, C[2]/(x y)]  *)

dsolvePotentialPDE[pde, f[x, y], {x, y}]
(*  f[x, y] -> C[2]/(x y)  *)

Not curl-free:

dsolvePotentialPDE[Thread[Grad[f[x, y, z], {x, y, z}] == x f[x, y, z]], f, {x, y, z}]

Mathematica graphics

Nonlinear:

dsolvePotentialPDE[Thread[Grad[f[x, y, z], {x, y, z}] == f[x, y, z]^2], f, {x, y, z}]
(*  1/(-x - y - z - C[3])  *)

Clues about DSolve

One way to Trace all methods might be given by the following:

allmeth = Join[
   DeleteCases[
    ToExpression@ DeleteCases[Names["DSolve`*"], 
      Alternatives["DSolve`DSolveParser", "DSolve`DSolveToPureFunction", 
       "DSolve`DSolveZeroQ", "DSolve`print", "DSolve`DSolveFlatten", 
       "DSolve`DSolveOuter", "DSolve`DSolveNonZeroQ",
       "DSolve`DSolveIntegrate"]], (* might want to keep this one sometimes! *)
    Except[_Symbol]],
   {DSolve`DSolvePDEDump`PDEFirstIntegrals, 
    DSolve`DSolveExtendedLibraryDump`QuadraticSolutionOfNonLinear2ndOrderODE, 
    DSolve`DSolveExtendedLibraryDump`CubicSolutionOfNonLinear2ndOrderODE, 
    DSolve`DSolveExtendedLibraryDump`InverseFunctionForSuccessiveReductionOfOrder, 
    DSolve`DSolveExtendedLibraryDump`SpecialOrder2BVP(*,
    DSolve`DSolveCauchyProblemOrder1PDEDump`SolveCauchyProblemForFirstOrderPDE*)}
   ];

And then, assuming the list is complete, the following will catch all the method calls:

Trace[
 DSolve[pde, {f}, {x, y}],
 Alternatives @@ Blank /@ allmeth // Evaluate,
 TraceInternal -> True
 ]

The results come wrapped in oodles of braces that represent the traversal of the stack by the computation. You can pretty it up a bit with the following two utilities:

ClearAll[strip, fmt];
fmt[L_List] := Column[fmt /@ L,
   Spacings -> {2, 0.8},
   Dividers -> {All, Thread[{1, -1} -> Black]~Join~Thread[Range[2, Length@L] -> Thin]},
   BaseStyle -> 12];
fmt[x_] := x;
strip[{L_List}] := strip@L;
strip[L_List] := strip /@ L;
strip[x_] := x;

Then on the OP's problem we get:

fmt@strip@Trace[
   DSolve[pde, {f}, {x, y}],
   Alternatives @@ Blank /@ allmeth // Evaluate,
   TraceInternal -> True
   ]

Mathematica graphics

We can Trace the last method, which was called by DSolve`DSolveDispatchPDEs, and see that pde is checked for f[x, y]:

fmt@Trace[
  DSolve`DSolvePDEs[{x*Derivative[1, 0][f][x, y] == -f[x, y], 
      y*Derivative[0, 1][f][x, y] == -f[x, y]}, {f}, {x, y}, C, 1],
  TraceInternal -> True
  ]

Mathematica graphics

Similarly one can trace the solution of a gradient/potential function problem and see that it is solve with successive integrations by DSolve`DSolveLinearPDE, except at the last step when it's an ODE and DSolve`DSolveDispatchODE is used.

fmt@strip@Trace[
   DSolve[
    Thread[Grad[f[x, y, z], {x, y, z}] == Grad[x^3 y^5 z^7, {x, y, z}]], 
    {f}, {x, y, z}],
   Alternatives @@ Blank /@ allmeth // Evaluate,
   TraceInternal -> True
   ]
$\endgroup$
1
  • $\begingroup$ sorry I clicked the wrong bounty button <.< I very much appreciate your answer, and automating the successive integration seems the way to do it. Will dig deeper into the issue when I get the chance. $\endgroup$
    – knl
    Commented May 19, 2017 at 12:05

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