Function Repository Resource:

ODENormalForm

Source Notebook

Transform a differential equation into normal form

Contributed by: Daniele Gregori

ResourceFunction["ODENormalForm"][ode]

returns the normal form of an ordinary differential equation.

ResourceFunction["ODENormalForm"][ode,y]

returns the normal form of an ordinary differential equation, with new dependent variable y.

Details and Options

This function allows one to obtain the normal form of an ordinary differential equation (ODE), by transforming the dependent variable so as to eliminate the next-to-leading derivative.
In general, for a linear ODE of order n: an(x)ϕ(n)(x)+an-1(x)ϕ(n-1)(x)+an-2(x)ϕ(n-2)(x)++a1(x)ϕ(x)+a0(x)ϕ(x)=0. By dividing by and transforming the dependent variable as: , the normal form of the ODE will read: Φ(n)(x)+bn-2(x)Φ(n-2)(x)++b1(x)Φ(x)+b0(x)Φ(x)=0, for some new coefficients bk(x).
This standard transformation goes also under the name of Abel transformation.
The output is a ConditionalExpression with the transformed equation as the first argument and the transformation of the dependent variable as the second argument.
ResourceFunction["ODENormalForm"] accepts the equation alone, in which case the new dependent variable is chosen automatically; otherwise one can specify a new symbol for the new dependent variable.
ResourceFunction["ODENormalForm"] accepts the following options:
"SimplifyEquation"Falseperforms further simplification of the equation
ResourceFunction["ODENormalForm"] sometimes improves the solution of the ODE (by giving a better input to DSolve). Furthermore, it allows one to map different equations into each other (by matching the parameters of the coefficients through SolveAlways).

Examples

Basic Examples (3) 

Transform an ODE into normal form:

In[1]:=
ResourceFunction[
 "ODENormalForm"][-((z^2 + \[Nu]^2) w[z]) + z Derivative[1][w][z] + z^2 Derivative[2][w][z] == 0]
Out[1]=

The second argument allows one to specify a new symbol for the new dependent variable:

In[2]:=
ResourceFunction[
 "ODENormalForm"][-a b w[z] + (c - (1 + a + b) z) Derivative[1][w][
     z] + (1 - z) z Derivative[2][w][z] == 0, f]
Out[2]=

The transformation of the dependent variable is obtained by applying Last:

In[3]:=
Last[%]
Out[3]=

Scope (4) 

The normal form can be obtained for equations of any order:

In[4]:=
ResourceFunction[
 "ODENormalForm"][\[Phi]''''[z] + 1/z \[Phi]'''[z] + 1/z^4 \[Phi]'[z] + 1/z^5 \[Phi][z] == 0]
Out[4]=

The equation is automatically simplified:

In[5]:=
ResourceFunction["ODENormalForm"][
 D[(1 - 1/r^2) D[(r - 1)/r^2 R[r], r], r] + R[r] == 0, \[CapitalPsi]]
Out[5]=

Apply the function to a non-linear equation:

In[6]:=
ResourceFunction[
 "ODENormalForm"][\[Phi]''[z] + 1/z \[Phi]'[z] + (\[Phi][z])^2 == 0]
Out[6]=

If the equation is already in normal form, the function still simplifies it:

In[7]:=
ResourceFunction["ODENormalForm"][
 w[z]/(z^(1/2) (z - 1)^(1/2)) + 4 z^(1/2) (z - 1)^(1/2) Derivative[2][w][z] == 0]
Out[7]=

Options (3) 

Let us consider the Appell equation:

In[8]:=
appelODE = MathematicalFunctionData["AppellF1", "DifferentialEquations"][[2, 2, 1, 1]] // ToExpression@
    StringReplace[ToString@FullForm[#], "\\[Formal" ~~ x_ ~~ "]" :> ToLowerCase[x]] &
Out[8]=

In normal form, by default it becomes a cumbersome expression:

In[9]:=
ResourceFunction["ODENormalForm"][appelODE] // EchoFunction[LeafCount] // Short[#, 8] &
Out[9]=

This result can be further simplified through the option "SimplifyEquation":

In[10]:=
ResourceFunction["ODENormalForm"][appelODE, "SimplifyEquation" -> True] // EchoFunction[LeafCount]
Out[10]=

Properties and Relations (4) 

We can check that the solution of the differential equation in the original form:

In[11]:=
DSolve[\[Phi]''[z] + 1/z \[Phi]'[z] + \[Phi][z] == 0]
Out[11]=

and in normal form:

In[12]:=
DSolve[ResourceFunction[
   "ODENormalForm"][\[Phi]''[z] + 1/z \[Phi]'[z] + \[Phi][z] == 0][[1]]]
Out[12]=

are mapped into each other by the dependent variable transformation:

In[13]:=
%%[[1, 1, 2]] == %[[1, 1, 2]]*(Most@
     ResourceFunction[
       "ODENormalForm"][\[Phi]''[z] + 1/z \[Phi]'[z] + \[Phi][z] == 0][[2, 2]]) // Simplify
Out[13]=

However, this cannot be immediately verified in all cases, since the linear combination of solutions may be chosen differently:

In[14]:=
DSolve[-a w[z] + (b - z) Derivative[1][w][z] + z Derivative[2][w][z] ==
   0]
Out[14]=
In[15]:=
DSolve[ResourceFunction[
   "ODENormalForm"][-a w[z] + (b - z) Derivative[1][w][z] + z Derivative[2][w][z] == 0]] // FullSimplify
Out[15]=

Possible Issues (2) 

For the moment, ODENormalForm works only for single variable (aka ordinary) linear differential equations:

In[16]:=
ResourceFunction["ODENormalForm"][
 D[\[Phi][z, y], {z, 2}] + D[\[Phi][z, y], y] + \[Phi][z, y] == 0]
Out[16]=

It can deal with some non-linear equations, but not in their leading or sub-leading coefficient:

In[17]:=
ResourceFunction[
 "ODENormalForm"][\[Phi]''[z] + 1/(z (z - 1)) (\[Phi]'[z])^2 + (\[Phi][z]) == 0]
Out[17]=

Neat Examples (5) 

Let us consider the Regge-Wheeler (RW) equation, governing axial perturbations of black holes:

In[18]:=
rwODE = With[{f = 1 - 2 M/# &}, f[r] D[f[r] D[#, r], r] &@\[Phi][r] + \[Omega]^2 \[Phi][r] - f[r] ((l (l + 1))/r^2 + (1 - s^2) (2 M)/r^3) \[Phi][r] == 0]
Out[18]=

As it is written, we can find its solution only in the unintuitive DifferentialRoot form:

In[19]:=
DSolve[rwODE]
Out[19]=

Let us try to transform this equation into normal form (and change independent variable as ), simply as:

In[20]:=
rwODENormal = ResourceFunction["ODENormalForm"][
  First@DSolveChangeVariables[
    Inactive[DSolve][rwODE, \[Phi][r], r], \[Phi][z], z, r == 2 M z]]
Out[20]=

Now, we can better find its solution in terms of the HeunC function:

In[21]:=
DSolve[rwODENormal[[1]]] // Simplify
Out[21]=

Let us also map the black hole parameters into the Confluent Heun ones, by transforming the latter equation into normal form:

In[22]:=
heunCODE = y''[z] + (\[Gamma]/z + \[Delta]/(z - 1) + \[Epsilon]) y'[
      z] + (\[Alpha] z - q)/(z (z - 1)) y[z] == 0;
In[23]:=
heunCODENormal = ResourceFunction["ODENormalForm"][heunCODE, \[CapitalPhi]]
Out[23]=
In[24]:=
SolveAlways[
   Coefficient[heunCODENormal[[1, 1]], \[CapitalPhi][z]] == Coefficient[rwODENormal[[1, 1]], \[CapitalPhi][z]], z] // MaximalBy[#, LeafCount][[1]] & // Column
Out[24]=

Publisher

Daniele Gregori

Requirements

Wolfram Language 13.1 (June 2022) or above

Version History

  • 1.0.0 – 12 February 2025

Related Resources

License Information