17  Sympy: Laplace Transform

Open In Colab

Used in the main text: Section 4.1 (Definition of the Laplace Transform), Section 12.15 (The Laplace Transform)

17.1 Importing sympy

import sympy
from sympy import (laplace_transform, inverse_laplace_transform,
                   laplace_correspondence, laplace_initial_conds, pi)
sympy.init_printing()

from IPython.display import display

print("sympy: ", sympy.__version__)
sympy:  1.14.0
  • In recent versions of sympy (1.13 and later), \(~\)sympy.laplace_transform can transform derivatives of unknown functions, \(~\)apply both translation theorems, \(~\)the scaling rule \(f(at)\), \(~\)and the rule for \(t^n f(t)\). \(~\)Two companion functions finish the job:

    • laplace_correspondence(expr, {f: F}) \(~\)replaces \(\mathcal{L}\{f(t)\}\) by \(F(s)\)

    • laplace_initial_conds(expr, t, {y: [y0, y1, ...]}) \(~\)substitutes the initial values \(y(0)\), \(y'(0)\), \(\dots\)

  • A few rules are still missing in sympy 1.14: \(~\)integrals \(\int_0^t\), \(~\)convolution integrals, \(~\)and products such as \(e^{ct}\, t^n f(t)\). \(~\)For these we use a small wrapper _laplace_transform_expansion.py, \(~\)whose function laplace_transform_() adds only those rules and otherwise calls sympy.laplace_transform

17.2 Symbols and Functions

a, b = sympy.symbols("a, b", positive=True)
n, m = sympy.symbols("n, m", positive=True, integer=True)

t, tau, tau1, tau2 = sympy.symbols("t, tau, tau1, tau2", positive=True)
s = sympy.symbols("s")

y, f, g = sympy.symbols("y, f, g", cls=sympy.Function)
Y, F, G = sympy.symbols("Y, F, G", cls=sympy.Function)
  • A short helper: \(~\)transform and write \(\mathcal{L}\{y\}\), \(\mathcal{L}\{f\}\), \(\mathcal{L}\{g\}\) as \(Y(s)\), \(F(s)\), \(G(s)\)
def Lt(expr):
    return laplace_correspondence(laplace_transform_(expr, t, s), {y: Y, f: F, g: G})

17.3 Laplace transform

laplace_transform(1, t, s, noconds=True)

\(\displaystyle \frac{1}{s}\)

laplace_transform(t, t, s, noconds=True)

\(\displaystyle \frac{1}{s^{2}}\)

laplace_transform(sympy.exp(-3 *t), t, s, noconds=True)

\(\displaystyle \frac{1}{s + 3}\)

laplace_transform(2 *sympy.Heaviside(t -3), t, s, noconds=True)

\(\displaystyle \frac{2 e^{- 3 s}}{s}\)

laplace_transform(sympy.sin(2 *t)**2, t, s, noconds=True)

\(\displaystyle \frac{8}{s \left(s^{2} + 16\right)}\)

17.4 Inverse Laplace transform

inverse_laplace_transform(1 / s**3, s, t)

\(\displaystyle \frac{t^{2}}{2}\)

inverse_laplace_transform((-2 *s +6) / (s**2 + 4), s, t)

\(\displaystyle 3 \sin{\left(2 t \right)} - 2 \cos{\left(2 t \right)}\)

17.5 Laplace transform of derivatives

eq = y(t).diff(t, 2) +2 *y(t).diff(t) +10 *y(t)
eq

\(\displaystyle 10 y{\left(t \right)} + 2 \frac{d}{d t} y{\left(t \right)} + \frac{d^{2}}{d t^{2}} y{\left(t \right)}\)

Leq = laplace_correspondence(laplace_transform(eq, t, s, noconds=True), {y: Y})
Leq

\(\displaystyle s^{2} Y{\left(s \right)} + 2 s Y{\left(s \right)} - s y{\left(0 \right)} + 10 Y{\left(s \right)} - 2 y{\left(0 \right)} - \left. \frac{d}{d t} y{\left(t \right)} \right|_{\substack{ t=0 }}\)

Leq = laplace_initial_conds(Leq, t, {y: [1, 1]})   # y(0) = 1, y'(0) = 1
Leq

\(\displaystyle s^{2} Y{\left(s \right)} + 2 s Y{\left(s \right)} - s + 10 Y{\left(s \right)} - 3\)

sol = sympy.solve(Leq, Y(s))[0]
sol

\(\displaystyle \frac{s + 3}{s^{2} + 2 s + 10}\)

17.6 Laplace transform of integrals

eq = sympy.integrate(g(tau1), (tau1, 0, t)) +sympy.integrate(sympy.integrate(f(tau2), (tau2, 0, tau1)), (tau1, 0, t))
eq

\(\displaystyle \int\limits_{0}^{t} g{\left(\tau_{1} \right)}\, d\tau_{1} + \int\limits_{0}^{t}\int\limits_{0}^{\tau_{1}} f{\left(\tau_{2} \right)}\, d\tau_{2}\, d\tau_{1}\)

  • sympy.laplace_transform leaves these integrals unevaluated:
laplace_transform(eq, t, s, noconds=True)

\(\displaystyle \mathcal{L}_{t}\left[\int\limits_{0}^{t} g{\left(\tau_{1} \right)}\, d\tau_{1}\right]\left(s\right) + \mathcal{L}_{t}\left[\int\limits_{0}^{t}\int\limits_{0}^{\tau_{1}} f{\left(\tau_{2} \right)}\, d\tau_{2}\, d\tau_{1}\right]\left(s\right)\)

  • The wrapper uses \(~\mathcal{L}\left\{\int_0^t h(\tau)\,d\tau\right\} = \frac{1}{s}\mathcal{L}\{h(t)\}\):
Lt(eq)

\(\displaystyle \frac{G{\left(s \right)}}{s} + \frac{F{\left(s \right)}}{s^{2}}\)

17.7 Convolution

eq = sympy.integrate(f(tau)*g(t -tau), (tau, 0, t)) +sympy.integrate(f(t -tau)*g(tau), (tau, 0, t))
eq

\(\displaystyle \int\limits_{0}^{t} f{\left(\tau \right)} g{\left(t - \tau \right)}\, d\tau + \int\limits_{0}^{t} f{\left(t - \tau \right)} g{\left(\tau \right)}\, d\tau\)

Lt(eq)

\(\displaystyle 2 F{\left(s \right)} G{\left(s \right)}\)

Lt(sympy.integrate(sympy.exp(tau) *sympy.sin(t -tau), (tau, 0, t)))  # e^t * sin t

\(\displaystyle - \frac{s}{2 \left(s^{2} + 1\right)} - \frac{1}{2 \left(s^{2} + 1\right)} + \frac{1}{2 \left(s - 1\right)}\)

Used in the main text: Section 4.4 (Additional Operational Properties)

17.8 First translation theorem

eq = y(t) *sympy.exp(-a *t)
eq

\(\displaystyle y{\left(t \right)} e^{- a t}\)

Lt(eq)

\(\displaystyle Y{\left(a + s \right)}\)

17.9 Second translation theorem

eq = f(t -a) *sympy.Heaviside(t -a)
eq

\(\displaystyle f{\left(- a + t \right)} \theta\left(- a + t\right)\)

Lt(eq)

\(\displaystyle F{\left(s \right)} e^{- a s}\)

eq = g(t) *sympy.Heaviside(t -a)
eq

\(\displaystyle g{\left(t \right)} \theta\left(- a + t\right)\)

Lt(eq)   # alternative form: exp(-a s) L{g(t + a)}

\(\displaystyle \mathcal{L}_{t}\left[g{\left(a + t \right)}\right]\left(s\right) e^{- a s}\)

eq = sympy.cos(t) *sympy.Heaviside(t -pi)
eq

\(\displaystyle \cos{\left(t \right)} \theta\left(t - \pi\right)\)

Lt(eq)

\(\displaystyle - \frac{s e^{- \pi s}}{s^{2} + 1}\)

17.10 Derivatives of Transforms

eq = 3 *f(t) *t**m *t**n
eq

\(\displaystyle 3 t^{m} t^{n} f{\left(t \right)}\)

Lt(eq)

\(\displaystyle 3 \left(-1\right)^{m + n} \frac{d^{m + n}}{d s^{m + n}} F{\left(s \right)}\)

eq =  t**n *t**m *f(t)*sympy.exp(-2*t)
eq

\(\displaystyle t^{m} t^{n} f{\left(t \right)} e^{- 2 t}\)

Lt(eq)

\(\displaystyle \left(-1\right)^{m + n} \left. \frac{d^{m + n}}{d \sigma^{m + n}} F{\left(\sigma \right)} \right|_{\substack{ \sigma=s + 2 }}\)


eq = t**n *sympy.diff(f(t), t, t)
eq

\(\displaystyle t^{n} \frac{d^{2}}{d t^{2}} f{\left(t \right)}\)

Lt(eq)

\(\displaystyle \left(-1\right)^{n} \frac{d^{n}}{d s^{n}} \left(s^{2} F{\left(s \right)} - s f{\left(0 \right)} - \left. \frac{d}{d t} f{\left(t \right)} \right|_{\substack{ t=0 }}\right)\)

eq = t *sympy.integrate(f(tau), (tau, 0, t))
eq

\(\displaystyle t \int\limits_{0}^{t} f{\left(\tau \right)}\, d\tau\)

Lt(eq)

\(\displaystyle - \frac{\frac{d}{d s} F{\left(s \right)}}{s} + \frac{F{\left(s \right)}}{s^{2}}\)


eq = t *f(t) *sympy.exp(-t)
eq

\(\displaystyle t f{\left(t \right)} e^{- t}\)

Lt(eq)

\(\displaystyle - \left. \frac{d}{d \sigma} F{\left(\sigma \right)} \right|_{\substack{ \sigma=s + 1 }}\)

  • For this product, \(~\)sympy.laplace_transform itself (1.14) raises an error:
try:
    laplace_transform(eq, t, s)
except ValueError as err:
    print("ValueError:", err)
ValueError: 
Can't calculate derivative wrt s + 1.

eq = t *sympy.diff(f(t), t) *sympy.exp(-4*t)
eq

\(\displaystyle t e^{- 4 t} \frac{d}{d t} f{\left(t \right)}\)

Lt(eq)

\(\displaystyle - \left(s + 4\right) \left. \frac{d}{d \sigma} F{\left(\sigma \right)} \right|_{\substack{ \sigma=s + 4 }} - F{\left(s + 4 \right)}\)

Used in the main text: Section 4.4 (Additional Operational Properties)

17.11 Various Transform Examples

eq = sympy.exp(-a*t) *sympy.diff(f(t), t, t)
eq

\(\displaystyle e^{- a t} \frac{d^{2}}{d t^{2}} f{\left(t \right)}\)

Lt(eq)

\(\displaystyle \left(a + s\right)^{2} F{\left(a + s \right)} - \left(a + s\right) f{\left(0 \right)} - \left. \frac{d}{d t} f{\left(t \right)} \right|_{\substack{ t=0 }}\)


eq = sympy.exp(-4*t) *sympy.integrate(f(tau), (tau, 0, t))
eq

\(\displaystyle e^{- 4 t} \int\limits_{0}^{t} f{\left(\tau \right)}\, d\tau\)

Lt(eq)

\(\displaystyle \frac{F{\left(s + 4 \right)}}{s + 4}\)


eq = f(3*a*t)
eq

\(\displaystyle f{\left(3 a t \right)}\)

Lt(eq)   # scaling: F(s/k)/k with k = 3a

\(\displaystyle \frac{F{\left(\frac{s}{3 a} \right)}}{3 a}\)

\(~\)

17.12 Example 1

A technique that can be used to solve certain ODE problems is to Laplace transform the ODE, which for many problems results in an algebraic equation that is easier to solve. The solution to the algebraic equation can then be transformed back to the original domain with an inverse Laplace transform, to obtain the solution to the original problem. For example, consider the following differential equation for a driven harmonic oscillator:

\[ \frac{d^2 y}{dt^2} + 2\frac{dy}{dt} +10 y = 2\sin 3t \]

\(~\)

t = sympy.symbols('t', positive=True)
s = sympy.symbols('s')
y, Y = sympy.symbols('y, Y', cls=sympy.Function)
ode = y(t).diff(t, 2) +2 *y(t).diff(t) +10 *y(t) -2 *sympy.sin(3*t)
ode

\(\displaystyle 10 y{\left(t \right)} - 2 \sin{\left(3 t \right)} + 2 \frac{d}{d t} y{\left(t \right)} + \frac{d^{2}}{d t^{2}} y{\left(t \right)}\)

Lode = laplace_correspondence(laplace_transform(ode, t, s, noconds=True), {y: Y})
Lode

\(\displaystyle s^{2} Y{\left(s \right)} + 2 s Y{\left(s \right)} - s y{\left(0 \right)} + 10 Y{\left(s \right)} - 2 y{\left(0 \right)} - \left. \frac{d}{d t} y{\left(t \right)} \right|_{\substack{ t=0 }} - \frac{6}{s^{2} + 9}\)

  • At this point, we need to specify the initial conditions for the ODE problem. Here we use \(y(0)=1\) and \(y'(0)=0\), and substitute them into the Laplace-transformed ODE with laplace_initial_conds:
Lode = laplace_initial_conds(Lode, t, {y: [1, 0]})
Lode

\(\displaystyle s^{2} Y{\left(s \right)} + 2 s Y{\left(s \right)} - s + 10 Y{\left(s \right)} - 2 - \frac{6}{s^{2} + 9}\)

  • This is an algebraic equation that can be solved for \(Y(s)\)
Ysol = sympy.solve(Lode, Y(s))
Ysol[0]

\(\displaystyle \frac{s^{3} + 2 s^{2} + 9 s + 24}{s^{4} + 2 s^{3} + 19 s^{2} + 18 s + 90}\)

  • The result is a list of solutions, which in this case contains only one element. Performing the inverse Laplace transformation on this expression gives the solution to the original problem in the time domain (older versions of sympy needed the partial fractions to be inverted one by one):
ysol = inverse_laplace_transform(Ysol[0], s, t)
ysol.expand().collect([sympy.exp(-t)])

\(\displaystyle \left(\frac{43 \sin{\left(3 t \right)}}{111} + \frac{49 \cos{\left(3 t \right)}}{37}\right) e^{- t} + \frac{2 \sin{\left(3 t \right)}}{37} - \frac{12 \cos{\left(3 t \right)}}{37}\)

  • The same result is obtained with dsolve:
sympy.dsolve(ode, y(t), ics={y(0): 1, y(t).diff(t).subs(t, 0): 0})

\(\displaystyle y{\left(t \right)} = \left(\frac{43 \sin{\left(3 t \right)}}{111} + \frac{49 \cos{\left(3 t \right)}}{37}\right) e^{- t} + \frac{2 \sin{\left(3 t \right)}}{37} - \frac{12 \cos{\left(3 t \right)}}{37}\)

Used in the main text: Section 4.2 (The Inverse Transform and Transforms of Derivatives)

17.13 Example 2

In some instances, the Laplace transform can be used to solve linear differential equations with variable monomial coefficients

\[ty'' + y' +ty =0, \;\;y(0)=1, \;y'(0)=0\]

\(~\)

s, t = sympy.symbols('s, t', positive=True)
y, Y = sympy.symbols('y, Y', cls=sympy.Function)

ode = t *y(t).diff(t, 2) +y(t).diff(t) +t *y(t)
ode

\(\displaystyle t y{\left(t \right)} + t \frac{d^{2}}{d t^{2}} y{\left(t \right)} + \frac{d}{d t} y{\left(t \right)}\)

\(~\)

  • Reduce the given differential equation to a linear first-order DE in the transformed function \(Y(s)=\mathcal{L}_t\left[y(t)\right]\). \(~\)Since \(\mathcal{L}\{t\,h(t)\} = -\frac{d}{ds}\mathcal{L}\{h(t)\}\), \(~\)the factor \(t\) becomes a derivative with respect to \(s\)
Lode = laplace_transform(ode, t, s, noconds=True)
Lode = laplace_initial_conds(laplace_correspondence(Lode, {y: Y}), t, {y: [1, 0]})
Lode = Lode.doit().expand().collect(Y(s).diff(s))
Lode

\(\displaystyle - s Y{\left(s \right)} + \left(- s^{2} - 1\right) \frac{d}{d s} Y{\left(s \right)}\)

  • Solve the first-order ODE for \(Y(s)\) and then find \(y(t)=\mathcal{L}_t^{-1} \left[Y(s) \right]\)
sol = sympy.dsolve(Lode, Y(s))
sol

\(\displaystyle Y{\left(s \right)} = \frac{C_{1}}{\sqrt{s^{2} + 1}}\)

y = inverse_laplace_transform(sol.rhs, s, t)
y

\(\displaystyle C_{1} J_{0}\left(t\right)\)

  • The constant follows from \(y(0)=1\) \(~\)(note \(J_0(0)=1\))
C1 = sympy.solve(sympy.Eq(y.subs(t, 0), 1))[0]
y.subs(sympy.Symbol('C1'), C1)

\(\displaystyle J_{0}\left(t\right)\)

Used in the main text: Section 4.4 (Additional Operational Properties)