# SLiCAP Laplace analysis

Symbolic complex frequency domain analysis provides:

- A Laplace transfer function

  This is the Laplace Transform of the unit impulse response from a circuit having the signal source as input signal, and the detector voltage or current as output signal.

- The Laplace Transform of a detector voltage or current with the voltages and currents of the independent sources as input signals.

  Important

  The `value` parameter of independent sources must be specified in the Laplace domain:

  Some examples of time functions and their Laplace Transform:

  1. Impulse: \(a \delta (t) \Leftrightarrow a\)

  2. Step: \(a \mu (t) \Leftrightarrow \frac{a}{s}\)

  3. Linear ramp: \(at \Leftrightarrow \frac{a}{s^2}\)

  4. Sine: \(a\sin{(\omega t)} \Leftrightarrow \frac{a \omega}{s^2+\omega^2}\)

  5. Cosine: \(a\cos{(\omega t)} \Leftrightarrow \frac{a s}{s^2+\omega^2}\)

  6. Real exponential: \(a \exp{(-At)} \Leftrightarrow \frac{a}{s+A}\)

  7. Use Laplace output of other circuits as input

  8. Use built-in filter Laplace filter responses as input

SLiCAP can perform mixed symbolic/numeric Laplace analysis and has a number of supporting functions for processing the results.

SLiCAP output displayed below, is generated with the script: `laplace.py`, imported by `Manual.py`.

    1#!/usr/bin/env python3
    2# -*- coding: utf-8 -*-
    3
    4"""
    5laplace.py: SLiCAP scripts for the HTML help file
    6"""
    7import SLiCAP as sl

## Perform Laplace analysis

### Create a circuit object

    10passive_network = sl.makeCircuit("kicad/myPassiveNetwork/myPassiveNetwork.kicad_sch")

### Obtain the Laplace Transform of the gain

The function doLaplace() returns the Laplace Transform of a transfer, or of a detector voltage or current.

Below a script for obtaining the Laplace Transform of the source-detector transfer (gain) of the circuit.

    12# Obtain the transfer from source to detector
    13result = sl.doLaplace(passive_network)
    14gain   = result.laplace
    15print(gain)

Note

- The default value of the keyword argument `transfer` is `gain`, which is the transfer from source to load.

- By default, the source and the detector as defined in the circuit will be used.

- The default value of the keyword argument `pardefs` is `None`. Hence, parameter definitions will not be substituted in element expressions.

- The default value of the keyword argument `numeric` is `None`. Hence, no numeric evaluation takes place.

- The default value of the keyword argument `convtype` is `None`. Hence, no differential-mode or common-mode conversion will be applied.

The result is shown below.

    R_ell*(C_b*L*s**2 + 1)/((R_ell + R_s)*(C_a*C_b*L*R_ell*R_s*s**3/(R_ell + R_s) + s**2*(C_a*L*R_ell + C_b*L*R_ell + C_b*L*R_s)/(R_ell + R_s) + s*(C_a*R_ell*R_s + L)/(R_ell + R_s) + 1))

This can be typesetted using the formatter. The script below shows how to create an RST snippet for the source of this help file.

    70filt.label    = "gain"
    71
    72# Create the plot
    73sl.plotSweep("BP4", "3-rd order Chebyshev type 1 band-pass, 1 dB ripple",

\[\frac{V_{out}}{V_{V1}} = \frac{R_{\ell} \left(C_{b} L s^{2} + 1\right)}{\left(R_{\ell} + R_{s}\right) \left(\frac{C_{a} C_{b} L R_{\ell} R_{s} s^{3}}{R_{\ell} + R_{s}} + \frac{s^{2} \left(C_{a} L R_{\ell} + C_{b} L R_{\ell} + C_{b} L R_{s}\right)}{R_{\ell} + R_{s}} + \frac{s \left(C_{a} R_{\ell} R_{s} + L\right)}{R_{\ell} + R_{s}} + 1\right)}\]

Numeric evaluation after subsititution of all the parameters:

    26# Same, but with parameter substitution and conversion to floats
    27gain_numeric = sl.doLaplace(passive_network, pardefs="circuit", numeric=True).laplace
    28print(gain_numeric)

Below the result.

    (455945326390521*s**2/500000000000000000000000000000 + 9/10)/(455945326390521*s**3/400000000000000000000000000000000000 + 1175660591821169*s**2/50000000000000000000000000000 + 1127*s/1000000000 + 1)

SLiCAP uses rational numbers. Typsetting functions convert them into float. The number of digits is set with `ini.disp`

    >>> import SLiCAP as sl
    >>> sl.ini.disp

    4

Below the typesetted numeric transfer:

\[\frac{V_{out}}{V_{V1}} = \frac{9.119 \cdot 10^{-16} s^{2} + 0.9}{1.14 \cdot 10^{-21} s^{3} + 2.351 \cdot 10^{-14} s^{2} + 1.127 \cdot 10^{-6} s + 1}\]

### Obtain the Laplace Transform of a detector voltage or current

With the keyword argument `transfer=None`, SLiCAP evaluates the response at the detector. The vector with independent variables now contains the values of all independent sources defined in the circuit. In this case we have \(V_\mathrm{V1}=\frac{1}{s}\).

    28vout_laplace = sl.doLaplace(passive_network, transfer=None).laplace

\[V_{out} = \frac{R_{\ell} \left(C_{b} L s^{2} + 1\right)}{a \left(R_{\ell} + R_{s}\right) \left(\frac{C_{a} C_{b} L R_{\ell} R_{s} s^{5}}{R_{\ell} a^{2} + R_{s} a^{2}} + \frac{s^{4} \left(C_{a} L R_{\ell} + C_{b} L R_{\ell} + C_{b} L R_{s}\right)}{R_{\ell} a^{2} + R_{s} a^{2}} + \frac{s^{3} \left(C_{a} C_{b} L R_{\ell} R_{s} a^{2} + C_{a} R_{\ell} R_{s} + L\right)}{R_{\ell} a^{2} + R_{s} a^{2}} + \frac{s^{2} \left(C_{a} L R_{\ell} a^{2} + C_{b} L R_{\ell} a^{2} + C_{b} L R_{s} a^{2} + R_{\ell} + R_{s}\right)}{R_{\ell} a^{2} + R_{s} a^{2}} + \frac{s \left(C_{a} R_{\ell} R_{s} + L\right)}{R_{\ell} + R_{s}} + 1\right)}\]

## Return attributes

Important

The evaluation of the Laplace Transform, requires:

1. Creation of the matrix equation

2. Evaluation of the denominator `doDenom()`

3. Evaluation of the numerator `doNumer()`

4. Evaluation of numerator/denominator

This returns the following result attributes:

    17M        = result.M
    18Iv       = result.Iv
    19Dv       = result.Dv
    20denom    = result.denom
    21numer    = result.numer
    22transfer = result.laplace

The matrix equation is for `transfer="gain"`: the value of the signal source has been set to unity:

\[\begin{split}\left[\begin{matrix}0\\1\\0\\0\\0\end{matrix}\right]=\left[\begin{matrix}- L s & 0 & 1 & 0 & -1\\0 & 0 & 0 & 1 & 0\\1 & 0 & C_{b} s + \frac{1}{R_{s}} & - \frac{1}{R_{s}} & - C_{b} s\\0 & 1 & - \frac{1}{R_{s}} & \frac{1}{R_{s}} & 0\\-1 & 0 & - C_{b} s & 0 & C_{a} s + C_{b} s + \frac{1}{R_{\ell}}\end{matrix}\right]\cdot \left[\begin{matrix}I_{L1}\\I_{V1}\\V_{1}\\V_{in}\\V_{out}\end{matrix}\right]\end{split}\]

## Related math functions

### Coefficients of a Laplace rational function

The function coeffsTransfer() returns a tupe with:

1. A gain factor = normalization coefficient.

2. A list with normalized numerator coefficients of the Laplace variable `ini.laplace=s` in ascending order.

   Normalization: the lowest order nonzero coefficient is normalized to unity.

3. A list with normalized denominator coefficients of the Laplace variable `ini.laplace=s` in ascending order.

   Normalization: the lowest order nonzero coefficient is normalized to unity.

    30gain_factor, numer_coeffs, denom_coeffs = sl.coeffsTransfer(gain)
    31print("\nGain factor: ", gain_factor)
    32print("\nNumerator coefficients of 's':")
    33for i in range(len(numer_coeffs)):
    34    print(str(i), numer_coeffs[i])
    35print("\nDenominator coefficients of 's':")
    36for i in range(len(denom_coeffs)):
    37    print(str(i), denom_coeffs[i])

This yields:

    Gain factor: R_ell/(R_ell + R_s)

    Numerator coefficients of 's':
    0 1
    1 0
    2 C_b*L

    Denominator coefficients of 's':
    0 1
    1 (C_a*R_ell*R_s + L)/(R_ell + R_s)
    2 L*(C_a*R_ell + C_b*R_ell + C_b*R_s)/(R_ell + R_s)
    3 C_a*C_b*L*R_ell*R_s/(R_ell + R_s)

Typesetted:

Gain factor:\(\frac{R_{\ell}}{R_{\ell} + R_{s}}\)

Table 13 Normalized coefficients of ‘gain’.
Coeff | Value
--- | ---
\(b_{0}\) | \(1\)
\(b_{1}\) | \(0\)
\(b_{2}\) | \(C_{b} L\)
\(a_{0}\) | \(1\)
\(a_{1}\) | \(\frac{C_{a} R_{\ell} R_{s} + L}{R_{\ell} + R_{s}}\)
\(a_{2}\) | \(\frac{L \left(C_{a} R_{\ell} + C_{b} R_{\ell} + C_{b} R_{s}\right)}{R_{\ell} + R_{s}}\)
\(a_{3}\) | \(\frac{C_{a} C_{b} L R_{\ell} R_{s}}{R_{\ell} + R_{s}}\)

### Obtain circuit component values using a prototype function

The function equateCoeffs() can be used to determine component values of a circuit, such that its transfer equals that of a prototype transfer (rational) function.

An example that demonstrates the design of a 4-th order Linkwitz-Riley passive loudspeaker cross-over filter can be downloaded from github: FilterDesign.

### Obtain a Butterworth polynomial

The function butterworthPoly(n) returns a normalized (\(\omega=1\) rad/s) Butterworth polynomial of the n-th order.

Below the code for obtaining a normalized Butterworth polynomial of the 4-th order.

    39# Obtain a normalized Butterworth polynomial of the 4th order
    40BuP = sl.butterworthPoly(4)

The results are shown below

\[P_{Bu} = \left(s^{2} + 0.7654 s + 1\right) \left(s^{2} + 1.848 s + 1\right)\]

### Obtain a Bessel polynomial

The function besselPoly(n) returns a normalized (\(\omega=1\) rad/s) Bessel polynomial of the n-th order.

Below the code for obtaining a normalized Bessel polynomial of the 4-th order.

    42# Obtain a normalized Bessel polynomial of the 4th order
    43BeP = sl.besselPoly(4)

This yields:

\[P_{Be} = 0.1902 s^{4} + 0.8997 s^{3} + 1.915 s^{2} + 2.114 s + 1\]

### Obtain a Chebyshev type 1 polynomial

The function chebyshev1Poly(n, ripple) returns a normalized (\(\omega=1\) rad/s) Chebyshev polynomial of the n-th order, with `ripple` dB pass-band ripple.

Below the code for obtaining a normalized Chebyshev polynomial of the 4-th order with 1 dB pass-band.

    45# Obtain a normalized Chebyshev polynomial of the 4th order
    46ChP = sl.chebyshev1Poly(4, 1)

This yields:

\[P_{Ch} = \left(1.17 s^{2} + 0.3039 s + 1\right) \left(4.13 s^{2} + 2.59 s + 1\right)\]

### Obtain a filter prototype function

The function filterFunc() returns a prototype filter function.

The script below returns low-pass, high-pass, band-pass, band-stop, * and *all-pass filter functions of the order 2, based on a Butterworth polynomial (maximally-flat magnitude chraracteristic).

    49# Obtain filter functions
    50n   = 2                # Order of the filter
    51f_h = sp.Symbol("f_h") # Low-frequency -3dB
    52f_l = sp.Symbol("f_l") # High-frequency -3dB
    53ftp = "Butterworth"    # Fiter characteristic
    54
    55LP = sl.filterFunc(ftp, "lp", n, f_low=f_l, f_high=f_h) # Low-pass
    56HP = sl.filterFunc(ftp, "hp", n, f_low=f_l, f_high=f_h) # High-pass
    57BP = sl.filterFunc(ftp, "bp", n, f_low=f_l, f_high=f_h) # Band-pass
    58BS = sl.filterFunc(ftp, "bs", n, f_low=f_l, f_high=f_h) # Band-stop
    59AP = sl.filterFunc(ftp, "ap", n, f_low=f_l, f_high=f_h) # All-pass

This yields:

\[F_{LP} = \frac{1}{1 + \frac{0.7071 s}{\pi f_{h}} + \frac{0.25 s^{2}}{\pi^{2} f_{h}^{2}}}\]
\[F_{HP} = \frac{0.25 s^{2}}{\pi^{2} f_{l}^{2} \left(1 + \frac{0.7071 s}{\pi f_{l}} + \frac{0.25 s^{2}}{\pi^{2} f_{l}^{2}}\right)}\]
\[F_{BP} = \frac{0.25 s^{2} \left(f_{h}^{2} - 2 f_{h} f_{l} + f_{l}^{2}\right)}{\pi^{2} f_{h}^{2} f_{l}^{2} \left(1 + \frac{0.7071 s \left(f_{h} - f_{l}\right)}{\pi f_{h} f_{l}} + \frac{0.0625 s^{4}}{\pi^{4} f_{h}^{2} f_{l}^{2}} + \frac{0.1768 s^{3} \left(f_{h} - f_{l}\right)}{\pi^{3} f_{h}^{2} f_{l}^{2}} + \frac{0.25 s^{2} \left(f_{h}^{2} + f_{l}^{2}\right)}{\pi^{2} f_{h}^{2} f_{l}^{2}}\right)}\]
\[F_{BS} = \frac{1 + \frac{0.5 s^{2}}{\pi^{2} f_{h} f_{l}} + \frac{0.0625 s^{4}}{\pi^{4} f_{h}^{2} f_{l}^{2}}}{1 + \frac{0.7071 s \left(f_{h} - f_{l}\right)}{\pi f_{h} f_{l}} + \frac{0.0625 s^{4}}{\pi^{4} f_{h}^{2} f_{l}^{2}} + \frac{0.1768 s^{3} \left(f_{h} - f_{l}\right)}{\pi^{3} f_{h}^{2} f_{l}^{2}} + \frac{0.25 s^{2} \left(f_{h}^{2} + f_{l}^{2}\right)}{\pi^{2} f_{h}^{2} f_{l}^{2}}}\]
\[F_{AP} = \frac{1 - \frac{0.7071 s}{\pi f_{h}} + \frac{0.25 s^{2}}{\pi^{2} f_{h}^{2}}}{1 + \frac{0.7071 s}{\pi f_{h}} + \frac{0.25 s^{2}}{\pi^{2} f_{h}^{2}}}\]

### Example 3-rd order Chebyshev type 1 bandpass 900 Hz-1200 Hz

    61# 3-rd order Chebyshev band-pass 900-1200 Hz
    62BP_num   = sp.N(sl.filterFunc("Chebyshev1", "bp", 3, f_low=900, f_high=1200,
    63                              ripple=1))
    64
    65# Plot with an instruction object, this comprises plot settings and results
    66filt          = sl.instruction()
    67filt.gainType = "gain"
    68filt.dataType = "laplace"
    69filt.laplace  = BP_num
    70filt.label    = "gain"
    71
    72# Create the plot
    73sl.plotSweep("BP4", "3-rd order Chebyshev type 1 band-pass, 1 dB ripple",
    74             filt, 0.1, 10, 500, sweepScale="k", funcType="dBmag", show=False)

The transfer function of this filter:

\[H_{s} = \frac{3.235 \cdot 10^{-14} s^{3}}{1.29 \cdot 10^{-23} s^{6} + 2.195 \cdot 10^{-20} s^{5} + 1.698 \cdot 10^{-15} s^{4} + 1.904 \cdot 10^{-12} s^{3} + 7.238 \cdot 10^{-8} s^{2} + 3.991 \cdot 10^{-5} s + 1}\]

### Inverse Laplace Transform

SLiCAP has a built-in symbolic and numeric Inverse Laplace Transform function ilt(). However, it is preferred to use time-domain analysis for this purpose.

Symbolic Inverse Laplace Transform is implemented for lower order rational functions and functions that are implemented in Sympy.

Numeric Inverse Laplace Transform is implemented for rational functions of any order.

## Display equations on HTML pages and in LaTeX documents

The report module Create reports, discusses how HTML snippets and LaTeX snippets can be created for variables, expressions, equations and tables.

As a matter of fact, all equations, tables and figures on this page are created with this module:

    76# Generate RST snippets for the Help file
    77rst = sl.RSTformatter()
    78
    79rst.eqn("V_out/V_V1", gain).save("eqn-laplace-passive")
    80rst.matrixEqn(result.Iv, result.M, result.Dv).save("eqn-matrix-passive")
    81rst.eqn("V_out/V_V1", gain_numeric).save("eqn-laplace-passive-numeric")
    82rst.eqn("V_out", vout_laplace).save("eqn-laplace-passive-vout")
    83rst.coeffsTransfer(sl.coeffsTransfer(gain),
    84                   caption="Normalized coefficients of 'gain'.").save("table-coeffs-gain")
    85rst.eqn("P_Bu", BuP,).save("eqn-BuP")
    86rst.eqn("P_Be", BeP,).save("eqn-BeP")
    87rst.eqn("P_Ch", ChP,).save("eqn-ChP")
    88rst.eqn("F_LP", LP,).save("eqn-FLP")
    89rst.eqn("F_HP", HP,).save("eqn-FHP")
    90rst.eqn("F_BP", BP,).save("eqn-FBP")
    91rst.eqn("F_BS", BS,).save("eqn-FBS")
    92rst.eqn("F_AP", AP,).save("eqn-FAP")
    93rst.eqn("H_s", BP_num).save("eqn-BP_num")

## Plot frequency characteristics

SLiCAP plot functions are discussed in Create plots