Pole Zero Maps

The Pole Zerp Map functions and classes allow users to create and animate Pole Zero Maps. The class supports both discrete-time and continuous-time systems

Static Examples

Example: Pzmap_Static_Example1

Pzmap_Static_Example1 Manim output
from manim import *
from controltheorylib import *

class Static_example1(Scene):
    def construct(self):

        # Define transfer function
        pzmap = PoleZeroMap("(s+1)/((s+2)*(s**2+s+10))")

        # Add title
        pzmap.title(r"H(s)=\frac{s+1}{(s+2)(s^2+s+10)}", use_math_tex=True)

        # Add statically to the scene
        self.add(pzmap)

Example: Pzmap_Static_Example2

Pzmap_Static_Example2 Manim output
from manim import *
from controltheorylib import *
import sympy as sp

class Pzmap_Static_Example2(Scene):
    def construct(self):

        s = sp.symbols('s')


        # Define transfer function, adjust ranges + increase size of markers to 0.18
        pzmap = PoleZeroMap(((s-2)/((s+3)*(s-10)*(s+13)*(s**2+5*s+8))),
                            x_range=[-15,13,4], y_range=[-2,2,1], markers_size=0.18)

        # Add stability regions, don't show unstable region,
        # change label of stable region to "ROC" (Region of convergence). Increase fill opacity
        pzmap.add_stability_regions(show_unstable=False,stable_label="ROC", fill_opacity=0.3)

        # Add statically to the scene
        self.add(pzmap)

Example: Pzmap_Static_Example3

Pzmap_Static_Example3 Manim output
from manim import *
from controltheorylib import *
import sympy as sp
class Pzmap_Static_Example3(Scene):
    def construct(self):

        s = sp.symbols('s') # define symbolic variable

        system = 1/(s**2+0.2*s+1) # symbolic expression
        system = ("1/(s**2+0.2*s+1)") # string
        system = ([1],[1,0.2,1]) # Coefficients

        # Define transfer function: use z to denote discrete-time system
        pzmap = PoleZeroMap("(z**2+2*z+1)/(z**2+0.25)")

        # Add title showing the system at hand
        pzmap.title(r"H(z)=\frac{z^2+2z+1}{z^2+\frac{1}{4}}", use_math_tex=True,)

        pzmap.add_stability_regions(unstable_label=r"\begin{cases} |z| > 1 \\ \text{unstable}  \end{cases}",
                                    use_mathtex=True, stable_label="")

        # Add statically to the scene
        self.add(pzmap)

Animation examples

Example: Pzmap_Animation

from manim import *
from controltheorylib import *

class Pzmap_Animation(Scene):
    def construct(self):

        # Define continuous-time system transfer function, turn dashed axis lines false (just straight lines)
        pzmap = PoleZeroMap(("(s-1)/(s+2)"), dashed_axis=False, x_range=[-3,3,1], y_range=[-3,3,1])

        pzmap.title(r"G(s)=\frac{s-1}{s+2}", use_math_tex=True, font_size=25)

        # Animate all plot components individually

        # Fade in the surrounding box
        self.play(FadeIn(pzmap.surrbox))
        self.wait(0.5) # wait 0.5 sec before animating the nex plot component

        # Animate ticks and their labels
        self.play(Create(pzmap.y_ticks), Create(pzmap.x_ticks), run_time=0.8)
        self.wait(0.5)
        self.play(Write(pzmap.y_tick_labels), Write(pzmap.x_tick_labels), run_time=0.8)
        self.wait(0.5)
        # Create non-dashed axis lines and their labels
        self.play(Create(pzmap.y_axis), Create(pzmap.x_axis))
        self.wait(0.5)
        self.play(Write(pzmap.axis_labels), Write(pzmap.title_text), run_time=0.8)
        self.wait(0.5)

        # Animate the pole and zero markers
        self.play(Create(pzmap.zeros), Create(pzmap.poles))
        self.wait(2.5)

Example: Stabilityregions

from manim import *
from controltheorylib import *
config.background_color = "#3d3d3d"
class Stabilityregions(Scene):
    def construct(self):

        # Define continuous-time system transfer function, turn dashed axis lines false (just straight lines)
        pzmap = PoleZeroMap(("(s-10)/(s*(s**2+6*s+5))"))

        pzmap.title(r"G(s)=\frac{s-100}{s(s^2+6s+5)}", use_math_tex=True, font_size=25)

        # Add stability regions, set add_directly to false
        # Such that it does not get added when we FadeIn all the plot components,
        # This way we can animate it seperatly.
        pzmap.add_stability_regions()

        # Instead of using pzmap, we use pzmap.basecomponents such that we can
        # animate the poles and zeros later
        self.play(FadeIn(pzmap.basecomponents))
        self.wait(2)
        # Fade in stable region
        self.play(FadeIn(pzmap.stable_region), Write(pzmap.text_stable))
        self.wait(1)
        # Fade in unstable region
        self.play(FadeIn(pzmap.unstable_region), Write(pzmap.text_unstable))
        self.wait(1)
        # Add poles and zeros
        self.play(GrowFromCenter(pzmap.zeros), GrowFromCenter(pzmap.poles), run_time=2)
        self.wait(2)

Example: Transform

from manim import *
from controltheorylib import *
config.background_color = "#3d3d3d"
class Transform(Scene):
    def construct(self):

        pzmap1 = PoleZeroMap(("(z-2)*(z+1)/(z**2+0.1*z+3)"), x_range=[-2,3,1], y_range=[-2,2,1])
        pzmap2 = PoleZeroMap(("(z-2)*(z+1)/(z**2+0.1*z+0.25)"), x_range=[-2,3,1], y_range=[-2,2,1])
        pzmap1.add_stability_regions()

        pzmap1.title(r"H_1(z)=\frac{(z-2)(z+1)}{z^2+0.1z+3}", use_math_tex=True, font_size=25)
        pzmap2.title(r"H_2(z)=\frac{(z-2)(z+1)}{z^2+0.1z+0.25}", use_math_tex=True, font_size=25)

        # Add first pzmap statically to the scene
        self.add(pzmap1)
        self.wait(2)

        # Transform first title into the other
        #self.play(ReplacementTransform(pzmap1.title_text, pzmap2.title_text))
        self.play(FadeOut(pzmap1.title_text))
        self.play(FadeIn(pzmap2.title_text))
        self.wait(1.5)
        self.play(ReplacementTransform(pzmap1.zeros, pzmap2.zeros), ReplacementTransform(pzmap1.poles, pzmap2.poles))
        self.wait(2)