Van der Pol phase portrait
Nonlinear Dynamics
Example from the compendium of canonical charts
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