Skip to content
Merged
173 changes: 173 additions & 0 deletions plots/smith-chart-basic/implementations/seaborn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
""" pyplots.ai
smith-chart-basic: Smith Chart for RF/Impedance
Library: seaborn 0.13.2 | Python 3.13.11
Quality: 90/100 | Created: 2026-01-15
"""

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns


# Set seaborn style for better aesthetics
sns.set_style("whitegrid")
sns.set_context("talk", font_scale=1.2)

# Reference impedance (ohms)
z0 = 50

# Generate frequency sweep data (1 GHz to 6 GHz, 50 points)
np.random.seed(42)
n_points = 50
freq_ghz = np.linspace(1, 6, n_points)

# Simulate a realistic impedance locus (antenna-like behavior)
# Create a spiral pattern that doesn't close on itself (avoids label overlap)
t = np.linspace(0, 1.8 * np.pi, n_points)
# Spiral-like pattern typical of antenna impedance vs frequency with drift
z_real = 50 + 30 * np.sin(t) + 15 * np.cos(2 * t) + 10 * (t / (2 * np.pi))
z_imag = 40 * np.sin(1.5 * t) + 20 * np.cos(t) - 15 * (t / (2 * np.pi))

# Normalize impedance to reference
z_norm = (z_real + 1j * z_imag) / z0

# Calculate reflection coefficient (Gamma)
gamma = (z_norm - 1) / (z_norm + 1)
gamma_real = gamma.real
gamma_imag = gamma.imag

# Create figure (square for Smith chart)
fig, ax = plt.subplots(figsize=(12, 12))

# Draw Smith chart grid
# Constant resistance circles
r_values = [0, 0.2, 0.5, 1, 2, 5]
theta = np.linspace(0, 2 * np.pi, 200)

for r in r_values:
# Circle center and radius in Gamma plane
center = r / (r + 1)
radius = 1 / (r + 1)
# Parametric circle
circle_x = center + radius * np.cos(theta)
circle_y = radius * np.sin(theta)
# Clip to unit circle
mask = circle_x**2 + circle_y**2 <= 1.001
ax.plot(circle_x[mask], circle_y[mask], color="#306998", linewidth=1)
# Add r value labels on the real axis (inside unit circle)
if r > 0:
label_x = r / (r + 1) - 1 / (r + 1) + 0.02 # Left edge of circle
if label_x > -0.95:
ax.text(label_x, 0.03, f"r={r}", fontsize=10, color="#306998", va="bottom")

# Constant reactance arcs
x_values = [0.2, 0.5, 1, 2, 5]

for x in x_values:
# Positive reactance arc (inductive)
center_y = 1 / x
radius = 1 / x
arc_theta = np.linspace(-np.pi / 2, np.pi / 2, 200)
arc_x = 1 + radius * np.cos(arc_theta)
arc_y = center_y + radius * np.sin(arc_theta)
mask = (arc_x**2 + arc_y**2 <= 1.001) & (arc_x >= -0.001)
ax.plot(arc_x[mask], arc_y[mask], color="#D4A017", linewidth=1.5)

# Negative reactance arc (capacitive)
arc_y_neg = -center_y + radius * np.sin(arc_theta)
mask_neg = (arc_x**2 + arc_y_neg**2 <= 1.001) & (arc_x >= -0.001)
ax.plot(arc_x[mask_neg], arc_y_neg[mask_neg], color="#D4A017", linewidth=1.5)

# Add x value labels inside the unit circle boundary
if x <= 2:
label_angle = np.arctan(1 / x)
label_x_pos = 0.85 * np.cos(label_angle)
label_y_pos = 0.85 * np.sin(label_angle)
ax.text(label_x_pos, label_y_pos + 0.03, f"x={x}", fontsize=10, color="#D4A017", va="bottom", ha="center")
ax.text(label_x_pos, -label_y_pos - 0.03, f"x=-{x}", fontsize=10, color="#D4A017", va="top", ha="center")

# Draw unit circle (|Gamma| = 1 boundary)
unit_theta = np.linspace(0, 2 * np.pi, 200)
ax.plot(np.cos(unit_theta), np.sin(unit_theta), color="#306998", linewidth=2.5)

# Draw horizontal axis (real axis)
ax.axhline(0, color="#306998", linewidth=1.5, alpha=0.6)

# Create DataFrame for seaborn plotting
df_locus = pd.DataFrame({"gamma_real": gamma_real, "gamma_imag": gamma_imag, "freq_ghz": freq_ghz})

# Plot impedance locus using seaborn lineplot for the trajectory
sns.lineplot(
data=df_locus, x="gamma_real", y="gamma_imag", color="#E74C3C", linewidth=3, ax=ax, legend=False, sort=False
)

# Add markers at key frequency points using seaborn scatterplot
key_indices = [0, n_points // 4, n_points // 2, 3 * n_points // 4, n_points - 1]
df_markers = df_locus.iloc[key_indices].copy()
sns.scatterplot(
data=df_markers,
x="gamma_real",
y="gamma_imag",
s=200,
color="#E74C3C",
edgecolor="white",
linewidth=2,
ax=ax,
zorder=10,
legend=False,
)

# Label key frequency points with smart positioning to avoid overlap
label_offsets = {
0: (12, -18),
n_points // 4: (15, 12),
n_points // 2: (12, -18),
3 * n_points // 4: (-60, 10),
n_points - 1: (-60, -15),
}

for idx in key_indices:
offset = label_offsets.get(idx, (10, 10))
ax.annotate(
f"{freq_ghz[idx]:.1f} GHz",
(gamma_real[idx], gamma_imag[idx]),
textcoords="offset points",
xytext=offset,
fontsize=14,
fontweight="bold",
color="#2C3E50",
)

# Mark the center (matched condition)
ax.scatter([0], [0], s=150, color="#27AE60", marker="+", linewidths=3, zorder=10)
ax.annotate(
"Z₀ (50Ω)", (0, 0), textcoords="offset points", xytext=(-40, -20), fontsize=14, color="#27AE60", fontweight="bold"
)

# Add VSWR circle (|Gamma| = 0.5, VSWR = 3:1)
vswr_radius = 0.5
vswr_circle_x = vswr_radius * np.cos(unit_theta)
vswr_circle_y = vswr_radius * np.sin(unit_theta)
ax.plot(vswr_circle_x, vswr_circle_y, "--", color="#9B59B6", linewidth=2)
ax.annotate("VSWR 3:1", (0.35, 0.35), fontsize=12, color="#9B59B6", fontweight="bold")

# Styling
ax.set_xlim(-1.15, 1.15)
ax.set_ylim(-1.15, 1.15)
ax.set_aspect("equal")
ax.set_xlabel("Real(Γ)", fontsize=20)
ax.set_ylabel("Imag(Γ)", fontsize=20)
ax.set_title("smith-chart-basic · seaborn · pyplots.ai", fontsize=24, fontweight="bold", pad=20)
ax.tick_params(axis="both", labelsize=16)
ax.grid(False) # Turn off default grid, we drew our own Smith grid

# Add legend with matching line styles
ax.plot([], [], color="#306998", linewidth=1, label="Constant R circles")
ax.plot([], [], color="#D4A017", linewidth=1.5, label="Constant X arcs")
ax.plot([], [], color="#E74C3C", linewidth=3, label="Impedance locus")
ax.plot([], [], color="#9B59B6", linewidth=2, linestyle="--", label="VSWR 3:1 circle")
ax.legend(loc="upper left", fontsize=14, framealpha=0.9)

plt.tight_layout()
plt.savefig("plot.png", dpi=300, bbox_inches="tight")
216 changes: 216 additions & 0 deletions plots/smith-chart-basic/metadata/seaborn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
library: seaborn
specification_id: smith-chart-basic
created: '2026-01-15T21:52:07Z'
updated: '2026-01-15T22:10:29Z'
generated_by: claude-opus-4-5-20251101
workflow_run: 21047488885
issue: 3792
python_version: 3.13.11
library_version: 0.13.2
preview_url: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/seaborn/plot.png
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/seaborn/plot_thumb.png
preview_html: null
quality_score: 90
review:
strengths:
- Excellent Smith chart grid implementation with proper constant R circles and X
arcs
- Smart frequency label positioning with manual offsets prevents overlap
- Complete feature set including VSWR circle and center marking
- Professional color scheme with good accessibility
- Title format exactly matches specification
weaknesses:
- Heavy reliance on matplotlib for grid drawing; seaborn only used for final locus
overlay
- Canvas utilization could be improved (square 12x12 on 16:9 target)
image_description: The plot displays a Smith chart for RF/impedance visualization.
The chart features a square 12x12 figure with proper aspect ratio. The outer boundary
is a dark blue unit circle representing |Γ|=1 (total reflection). Multiple constant
resistance circles (r=0.2, 0.5, 1, 2, 5) are drawn in blue, centered along the
horizontal real axis. Constant reactance arcs in gold/yellow (x=±0.2, ±0.5, ±1,
±2) curve from the right edge. A red impedance locus curve traces the frequency
sweep from 1.0 GHz to 6.0 GHz, with five key frequency markers (1.0, 3.2, 4.8,
3.6, 6.0 GHz) labeled in dark blue text. A dashed purple VSWR 3:1 circle (|Γ|=0.5)
is included. The center point is marked with a green '+' symbol and labeled 'Z₀
(50Ω)'. A legend in the upper left shows all four element types. Axes are labeled
'Real(Γ)' and 'Imag(Γ)' with appropriate font sizes.
criteria_checklist:
visual_quality:
score: 36
max: 40
items:
- id: VQ-01
name: Text Legibility
score: 10
max: 10
passed: true
comment: Title at 24pt, axis labels at 20pt, tick labels at 16pt, frequency
annotations at 14pt bold - all clearly readable
- id: VQ-02
name: No Overlap
score: 8
max: 8
passed: true
comment: Smart label positioning with custom offsets prevents all text overlap
- id: VQ-03
name: Element Visibility
score: 6
max: 8
passed: true
comment: Impedance locus clearly visible with linewidth=3, markers s=200;
grid lines could be slightly thinner
- id: VQ-04
name: Color Accessibility
score: 5
max: 5
passed: true
comment: Blue/gold/red/purple color scheme is colorblind-safe, high contrast
- id: VQ-05
name: Layout Balance
score: 3
max: 5
passed: true
comment: Square 12x12 figure with xlim/ylim ±1.15 provides good margins, moderate
canvas utilization
- id: VQ-06
name: Axis Labels
score: 2
max: 2
passed: true
comment: Real(Γ) and Imag(Γ) are descriptive for Smith chart context
- id: VQ-07
name: Grid & Legend
score: 2
max: 2
passed: true
comment: Default grid disabled, custom Smith grid drawn with appropriate line
weights, legend well-placed
spec_compliance:
score: 25
max: 25
items:
- id: SC-01
name: Plot Type
score: 8
max: 8
passed: true
comment: Correct Smith chart with constant resistance circles and reactance
arcs
- id: SC-02
name: Data Mapping
score: 5
max: 5
passed: true
comment: Impedance correctly normalized to Z₀=50Ω, gamma calculated properly
- id: SC-03
name: Required Features
score: 5
max: 5
passed: true
comment: 'All spec features: grid circles/arcs, impedance locus, frequency
labels, VSWR circle, center marking'
- id: SC-04
name: Data Range
score: 3
max: 3
passed: true
comment: Unit circle boundary properly shown with ±1.15 limits
- id: SC-05
name: Legend Accuracy
score: 2
max: 2
passed: true
comment: Legend correctly identifies all four element types
- id: SC-06
name: Title Format
score: 2
max: 2
passed: true
comment: 'Uses exact format: smith-chart-basic · seaborn · pyplots.ai'
data_quality:
score: 18
max: 20
items:
- id: DQ-01
name: Feature Coverage
score: 7
max: 8
passed: true
comment: Shows spiral impedance locus typical of antenna behavior, crosses
multiple grid regions
- id: DQ-02
name: Realistic Context
score: 7
max: 7
passed: true
comment: RF antenna impedance sweep 1-6 GHz is a realistic VNA measurement
scenario
- id: DQ-03
name: Appropriate Scale
score: 4
max: 5
passed: true
comment: Z₀=50Ω standard, impedance values reasonable for antenna (30-80Ω
range)
code_quality:
score: 8
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: 'Linear flow: imports → data → Smith grid → seaborn plots → styling
→ save'
- id: CQ-02
name: Reproducibility
score: 3
max: 3
passed: true
comment: np.random.seed(42) set
- id: CQ-03
name: Clean Imports
score: 0
max: 2
passed: false
comment: sns and pd imports present but seaborn barely used beyond lineplot/scatterplot
- id: CQ-04
name: No Deprecated API
score: 1
max: 1
passed: true
comment: All APIs current
- id: CQ-05
name: Output Correct
score: 1
max: 1
passed: true
comment: Saves as plot.png with dpi=300
library_features:
score: 3
max: 5
items:
- id: LF-01
name: Distinctive Features
score: 3
max: 5
passed: true
comment: Uses sns.lineplot and sns.scatterplot with DataFrames, sns.set_style/set_context
for theming, but heavy matplotlib reliance for grid drawing
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
- annotations
- patches
- manual-ticks
- custom-legend
patterns:
- data-generation
- iteration-over-groups
- explicit-figure
dataprep: []
styling:
- edge-highlighting
- grid-styling