Solve a simple system of partial differential equations

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?

• 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. – Michael E2 May 13 '17 at 2:39

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[y]/x]}} *)

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

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

which yields the result you provided.

• 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 – user46676 Apr 3 '17 at 14:34
• Thank you very much @anderstood. Do you think this is a shortcoming of Mathematica or is there something missing in my command? – knl Apr 4 '17 at 14:51
• @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. – anderstood Apr 4 '17 at 15:02
• +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. – Michael E2 May 13 '17 at 14:13

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 .";
dsolvePotentialPDE[pde : {__Equal}, f0_, vars_List] :=
Module[{f, grad, sol1, sol, eq2, integrate1D},
f = InternalProcessEquationsSimplifyDependent[{f0}, vars,
dsolvePotentialPDE, True][[1, 1]];
grad = Solve[pde, D[f @@ vars, #] & /@ vars];
If[Length@grad == 1 && Length /@ grad === {Length@vars},
integrate1D[{sols_, df_, {u_}, varsTBD_, varsDone_}] := (
sol1 = DSolve[Fold[ReplaceAll, df[], 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]}
);
sol = Catch[
First@Nest[
integrate1D,
{{}, grad, {f}, vars, {}},
Length@vars]
],
sol1 = $Failed; (* wrong kind of PDE *) ]; DSolveDSolveToPureFunction[ 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/(x y)]  *)

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

Not curl-free:

dsolvePotentialPDE[Thread[Grad[f[x, y, z], {x, y, z}] == x f[x, y, z]], f, {x, y, z}] 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)  *)

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

allmeth = Join[
DeleteCases[
ToExpression@ DeleteCases[Names["DSolve*"],
Alternatives["DSolveDSolveParser", "DSolveDSolveToPureFunction",
"DSolveDSolveZeroQ", "DSolveprint", "DSolveDSolveFlatten",
"DSolveDSolveOuter", "DSolveDSolveNonZeroQ",
"DSolveDSolveIntegrate"]], (* might want to keep this one sometimes! *)
Except[_Symbol]],
{DSolveDSolvePDEDumpPDEFirstIntegrals,
DSolveDSolveExtendedLibraryDumpQuadraticSolutionOfNonLinear2ndOrderODE,
DSolveDSolveExtendedLibraryDumpCubicSolutionOfNonLinear2ndOrderODE,
DSolveDSolveExtendedLibraryDumpInverseFunctionForSuccessiveReductionOfOrder,
DSolveDSolveExtendedLibraryDumpSpecialOrder2BVP(*,
DSolveDSolveCauchyProblemOrder1PDEDumpSolveCauchyProblemForFirstOrderPDE*)}
];

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
] We can Trace the last method, which was called by DSolveDSolveDispatchPDEs, and see that pde is checked for f[x, y]:

fmt@Trace[
DSolveDSolvePDEs[{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
] Similarly one can trace the solution of a gradient/potential function problem and see that it is solve with successive integrations by DSolveDSolveLinearPDE, except at the last step when it's an ODE and DSolveDSolveDispatchODE 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
]
• 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. – knl May 19 '17 at 12:05