Chris Parmer — home

Van der Pol phase portrait

Nonlinear Dynamics

Example from the compendium of canonical charts

Nonlinear Dynamics — Van der Pol phase portrait

Python Code

"""Nonlinear Dynamics — Van der Pol phase portrait."""


import numpy as np
import plotly.graph_objects as go
import plotly.figure_factory as ff
from scipy.integrate import solve_ivp


MU = 1.0


def vdp(t, y):
    x, v = y
    return [v, MU * (1 - x**2) * v - x]


def generate():
    ICs = [(0.1, 0), (3, 0), (-3, 0), (0.5, 2), (-2, -2)]
    colors = [VIOLET, GREEN, TEAL, PINK, TEAL]

    fig = go.Figure()

    # Background quiver field on a coarse grid
    gx = np.linspace(-4, 4, 16)
    gy = np.linspace(-4, 4, 16)
    GX, GY = np.meshgrid(gx, gy)
    GU = GY
    GV = MU * (1 - GX**2) * GY - GX

    # Normalize arrow length for display
    mag = np.sqrt(GU**2 + GV**2) + 1e-10
    scale_factor = 0.25
    GU_n = GU / mag * scale_factor
    GV_n = GV / mag * scale_factor

    quiver_fig = ff.create_quiver(
        GX, GY, GU_n, GV_n,
        scale=1.0, arrow_scale=0.3,
    )
    for trace in quiver_fig.data:
        trace.line.color = MUTED
        trace.opacity = 0.35
        trace.showlegend = False
        fig.add_trace(trace)

    # Trajectory for each IC
    t_span = (0, 30)
    t_eval = np.linspace(0, 30, 3000)

    for (x0, v0), color in zip(ICs, colors):
        sol = solve_ivp(vdp, t_span, [x0, v0], t_eval=t_eval, dense_output=True, rtol=1e-8, atol=1e-10)
        x_traj = sol.y[0]
        v_traj = sol.y[1]

        fig.add_trace(go.Scatter(
            x=x_traj, y=v_traj,
            mode="lines",
            name=f"IC=({x0},{v0})",
            line=dict(color=color, width=1.8),
            hovertemplate="x=%{x:.2f}, ẋ=%{y:.2f}<extra></extra>",
        ))

    fig.update_layout(
        xaxis=dict(
            title="x",
            range=[-4.5, 4.5],
            zeroline=True,
            zerolinecolor=GRID,
            zerolinewidth=1,
        ),
        yaxis=dict(
            title="dx/dt",
            range=[-4.5, 4.5],
            zeroline=True,
            zerolinecolor=GRID,
            zerolinewidth=1,
        ),
        legend=dict(orientation="h", y=1.08),
        margin=dict(t=40, b=60, l=70, r=40),
        height=580,
    )
    return fig


if __name__ == "__main__":
    generate()

Made with Plotly