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](https://cdn.statically.io/img/i.sstatic.net/tIEdl.png)
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](https://cdn.statically.io/img/i.sstatic.net/dyN9Q.png)
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](https://cdn.statically.io/img/i.sstatic.net/WC516.png)
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
]
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$