# 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 .";
integrate1D[{sols_, df_, {u_}, varsTBD_, varsDone_}] :=
Module[{sol1}
, 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]}
];
dsolvePotentialPDE[pde : {__Equal} | _Equal, f0_, vars_List] :=
, f = InternalProcessEquationsSimplifyDependent[
{f0}, vars, dsolvePotentialPDE, True
][[1, 1]]
; grad = Solve[pde, D[f @@ vars, {vars}]]
; sol =
Catch[First@
Nest[integrate1D, {{}, grad, {f}, vars, {}}, Length@vars]]
, sol = $$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)  *)


## Clues about DSolve

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},
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[