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?

  • 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


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.

  • 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


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.

dsolvePotentialPDE::irrot = "Vector field is not irrotational.";
dsolvePotentialPDE::dsolve = 
  "DSolve could not integrate component ``.";
integrate1D[{sols_, df_, {u_}, varsTBD_, varsDone_}] :=
   , 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 = 
       Nest[integrate1D, {{}, grad, {f}, vars, {}}, Length@vars]]
    , sol = $Failed(*wrong kind of PDE*)
     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


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[
    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! *)

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

 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:

   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]:

  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.

    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
  • $\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

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.