diff --git a/plots/smith-chart-basic/implementations/bokeh.py b/plots/smith-chart-basic/implementations/bokeh.py new file mode 100644 index 0000000000..5d1806369a --- /dev/null +++ b/plots/smith-chart-basic/implementations/bokeh.py @@ -0,0 +1,184 @@ +""" pyplots.ai +smith-chart-basic: Smith Chart for RF/Impedance +Library: bokeh 3.8.2 | Python 3.13.11 +Quality: 91/100 | Created: 2026-01-15 +""" + +import numpy as np +from bokeh.io import export_png, output_file, save +from bokeh.models import ColumnDataSource, Label +from bokeh.plotting import figure + + +# Reference impedance +Z0 = 50 # ohms + +# Create figure (square for Smith chart) +p = figure( + width=3600, + height=3600, + title="smith-chart-basic · bokeh · pyplots.ai", + x_axis_label="Real(Γ)", + y_axis_label="Imag(Γ)", + x_range=(-1.35, 1.35), + y_range=(-1.35, 1.35), + match_aspect=True, +) + +# Style settings +p.title.text_font_size = "32pt" +p.xaxis.axis_label_text_font_size = "24pt" +p.yaxis.axis_label_text_font_size = "24pt" +p.xaxis.major_label_text_font_size = "18pt" +p.yaxis.major_label_text_font_size = "18pt" + +# Smith chart grid colors +GRID_COLOR = "#808080" +GRID_ALPHA = 0.4 +BOUNDARY_COLOR = "#306998" + +# Draw unit circle (outer boundary - |Γ| = 1) +theta_circle = np.linspace(0, 2 * np.pi, 500) +unit_x = np.cos(theta_circle) +unit_y = np.sin(theta_circle) +p.line(unit_x, unit_y, line_width=3, line_color=BOUNDARY_COLOR, alpha=0.9) + +# Constant resistance circles: r = 0, 0.2, 0.5, 1, 2, 5 +r_values = [0, 0.2, 0.5, 1, 2, 5] +for r in r_values: + if r == 0: + # r=0 is the unit circle (already drawn) + continue + # Circle center at (r/(1+r), 0) with radius 1/(1+r) + center = r / (1 + r) + radius = 1 / (1 + r) + theta = np.linspace(0, 2 * np.pi, 300) + cx = center + radius * np.cos(theta) + cy = radius * np.sin(theta) + # Only keep points inside unit circle + mask = cx**2 + cy**2 <= 1.001 + cx, cy = cx[mask], cy[mask] + if len(cx) > 0: + p.line(cx, cy, line_width=1.5, line_color=GRID_COLOR, alpha=GRID_ALPHA) + +# Constant reactance arcs: x = ±0.2, ±0.5, ±1, ±2, ±5 +x_values = [0.2, 0.5, 1, 2, 5] +for x in x_values: + # Positive reactance arc: center at (1, 1/x) with radius |1/x| + center_y = 1.0 / x + radius = 1.0 / x + # Generate arc points + theta = np.linspace(-np.pi, np.pi, 500) + ax = 1.0 + radius * np.cos(theta) + ay = center_y + radius * np.sin(theta) + # Keep only points inside unit circle + mask = (ax**2 + ay**2 <= 1.001) & (ax >= -0.001) + ax, ay = ax[mask], ay[mask] + if len(ax) > 1: + # Sort by angle from center for smooth arc + angles = np.arctan2(ay - center_y, ax - 1.0) + order = np.argsort(angles) + p.line(ax[order], ay[order], line_width=1.5, line_color=GRID_COLOR, alpha=GRID_ALPHA) + + # Negative reactance arc: center at (1, -1/x) + center_y = -1.0 / x + radius = 1.0 / x + theta = np.linspace(-np.pi, np.pi, 500) + ax = 1.0 + radius * np.cos(theta) + ay = center_y + radius * np.sin(theta) + mask = (ax**2 + ay**2 <= 1.001) & (ax >= -0.001) + ax, ay = ax[mask], ay[mask] + if len(ax) > 1: + angles = np.arctan2(ay - center_y, ax - 1.0) + order = np.argsort(angles) + p.line(ax[order], ay[order], line_width=1.5, line_color=GRID_COLOR, alpha=GRID_ALPHA) + +# Draw horizontal axis (pure resistance line, x = 0) +p.line([-1, 1], [0, 0], line_width=2, line_color="#444444", alpha=0.6) + +# Generate example impedance data (antenna S11 sweep from 1-6 GHz) +np.random.seed(42) +n_points = 50 +freq = np.linspace(1e9, 6e9, n_points) # 1-6 GHz + +# Simulate realistic antenna impedance: resonance around 3.5 GHz +f_res = 3.5e9 +Q = 5 + +# Series RLC model: Z = R + jX +# Resistance peaks near resonance, reactance crosses zero at resonance +R = 45 + 10 * np.exp(-((freq - f_res) ** 2) / (0.5e9) ** 2) +X = Z0 * Q * (freq / f_res - f_res / freq) + 5 * np.sin(2 * np.pi * freq / 2e9) + +# Normalize impedance and convert to reflection coefficient Γ +z_norm = (R + 1j * X) / Z0 +gamma = (z_norm - 1) / (z_norm + 1) +gamma_real = np.real(gamma) +gamma_imag = np.imag(gamma) + +# Create data source for impedance locus +source = ColumnDataSource(data={"gamma_real": gamma_real, "gamma_imag": gamma_imag, "freq_ghz": freq / 1e9}) + +# Plot impedance locus curve +p.line("gamma_real", "gamma_imag", source=source, line_width=5, line_color="#FFD43B", alpha=0.9) +p.scatter( + "gamma_real", + "gamma_imag", + source=source, + size=14, + fill_color="#FFD43B", + line_color="#306998", + line_width=2, + alpha=0.85, +) + +# Add frequency labels at key points along the locus +label_indices = [0, n_points // 4, n_points // 2, 3 * n_points // 4, n_points - 1] +for idx in label_indices: + # Offset label position to avoid overlap with data points + offset_y = 0.08 if gamma_imag[idx] >= 0 else -0.12 + freq_label = Label( + x=gamma_real[idx], + y=gamma_imag[idx] + offset_y, + text=f"{freq[idx] / 1e9:.1f} GHz", + text_font_size="18pt", + text_color="#306998", + text_font_style="bold", + ) + p.add_layout(freq_label) + +# Mark the matched condition (center point: Z = Z0, Γ = 0) +p.scatter([0], [0], size=22, fill_color="#306998", line_color="white", line_width=3, alpha=0.95) +center_label = Label(x=0.06, y=0.06, text="Z=Z₀", text_font_size="20pt", text_color="#306998", text_font_style="bold") +p.add_layout(center_label) + +# Add resistance value labels on horizontal axis +for r in [0.2, 0.5, 1, 2]: + gamma_r = r / (1 + r) + r_label = Label(x=gamma_r, y=-0.1, text=f"r={r}", text_font_size="16pt", text_color="#666666", text_align="center") + p.add_layout(r_label) + +# Add reactance labels at chart boundary +for x in [0.5, 1, 2]: + # Calculate position on unit circle for positive x + # Intersection of x-arc with unit circle + angle = 2 * np.arctan(1 / x) + lx = np.cos(angle) + ly = np.sin(angle) + x_label = Label(x=lx + 0.05, y=ly + 0.02, text=f"x={x}", text_font_size="14pt", text_color="#666666") + p.add_layout(x_label) + # Negative x + x_label_neg = Label(x=lx + 0.05, y=-ly - 0.06, text=f"x=-{x}", text_font_size="14pt", text_color="#666666") + p.add_layout(x_label_neg) + +# Grid and background styling +p.grid.visible = False +p.background_fill_color = "#fafafa" +p.border_fill_color = "#fafafa" + +# Save PNG +export_png(p, filename="plot.png") + +# Save HTML for interactive viewing +output_file("plot.html", title="Smith Chart - bokeh - pyplots.ai") +save(p) diff --git a/plots/smith-chart-basic/metadata/bokeh.yaml b/plots/smith-chart-basic/metadata/bokeh.yaml new file mode 100644 index 0000000000..d77bd98a99 --- /dev/null +++ b/plots/smith-chart-basic/metadata/bokeh.yaml @@ -0,0 +1,226 @@ +library: bokeh +specification_id: smith-chart-basic +created: '2026-01-15T21:53:06Z' +updated: '2026-01-15T21:55:48Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 21047489015 +issue: 3792 +python_version: 3.13.11 +library_version: 3.8.2 +preview_url: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/bokeh/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/bokeh/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/bokeh/plot.html +quality_score: 91 +review: + strengths: + - Mathematically correct Smith chart implementation with proper constant-r circles + and constant-x arcs + - Excellent realistic RF engineering data scenario with antenna resonance model + - Clean visual hierarchy with yellow locus standing out against blue/gray grid + - 'Good use of Bokeh features: ColumnDataSource, Label annotations, both PNG and + HTML export' + - Proper normalization and gamma conversion mathematics + - Well-positioned frequency labels with offset to avoid overlap with data points + weaknesses: + - Could add HoverTool for interactive impedance/frequency inspection in the HTML + version + - Grid r/x labels are slightly small compared to other text elements + - Axis labels could include dimensionless clarification + image_description: The plot shows a Smith chart with the classic circular impedance + coordinate system. A blue outer boundary represents the unit circle (|Γ| = 1). + Gray constant resistance circles (r = 0.2, 0.5, 1, 2) are visible emanating from + the right side, and gray constant reactance arcs curve symmetrically above and + below the horizontal axis. A yellow impedance locus curve with circular markers + traces a frequency sweep from 1.0 GHz to 6.0 GHz, forming a characteristic loop + pattern in the right half of the chart. Frequency labels (1.0 GHz, 2.2 GHz, 3.6 + GHz, 4.8 GHz, 6.0 GHz) are placed at key points along the locus in blue text. + A dark blue marker at the center marks the matched condition (Z=Z₀). Small gray + labels indicate r-values (r=0.2, r=0.5, r=1, r=2) along the horizontal axis and + x-values (x=0.5, x=1, x=2) on the boundary arcs. The title "smith-chart-basic + · bokeh · pyplots.ai" appears at top-left. Axes are labeled "Real(Γ)" and "Imag(Γ)". + The background is light gray (#fafafa). + criteria_checklist: + visual_quality: + score: 36 + max: 40 + items: + - id: VQ-01 + name: Text Legibility + score: 9 + max: 10 + passed: true + comment: Title, axis labels, and frequency annotations are clearly readable + at high resolution. Some r/x labels on the grid are small but still legible. + - id: VQ-02 + name: No Overlap + score: 8 + max: 8 + passed: true + comment: No overlapping text elements; frequency labels are well-positioned + with offsets. + - id: VQ-03 + name: Element Visibility + score: 7 + max: 8 + passed: true + comment: Impedance locus markers (size=14) and line (width=5) are clearly + visible. The matched condition marker is prominent. + - id: VQ-04 + name: Color Accessibility + score: 5 + max: 5 + passed: true + comment: Yellow/gold (#FFD43B) locus against blue (#306998) boundary provides + excellent contrast. Gray grid lines are subtle. Colorblind-safe. + - id: VQ-05 + name: Layout Balance + score: 5 + max: 5 + passed: true + comment: Square aspect ratio is appropriate for Smith chart. Good utilization + of canvas with x_range/y_range (-1.35, 1.35) providing balanced margins. + - id: VQ-06 + name: Axis Labels + score: 1 + max: 2 + passed: true + comment: Labels Real(Γ) and Imag(Γ) are descriptive but lack explicit units + (dimensionless coefficient). + - id: VQ-07 + name: Grid & Legend + score: 1 + max: 2 + passed: true + comment: Grid alpha=0.4 is appropriate; no explicit legend but frequency labels + serve the purpose. + spec_compliance: + score: 24 + max: 25 + items: + - id: SC-01 + name: Plot Type + score: 8 + max: 8 + passed: true + comment: Correct Smith chart implementation with constant resistance circles + and reactance arcs. + - id: SC-02 + name: Data Mapping + score: 5 + max: 5 + passed: true + comment: Impedance correctly normalized (Z/Z0) and converted to reflection + coefficient Γ. + - id: SC-03 + name: Required Features + score: 4 + max: 5 + passed: true + comment: Has resistance circles, reactance arcs, impedance locus, frequency + labels, matched condition marker. VSWR circles noted as optional in spec + and not included. + - id: SC-04 + name: Data Range + score: 3 + max: 3 + passed: true + comment: Chart shows full unit circle with appropriate padding. + - id: SC-05 + name: Legend Accuracy + score: 2 + max: 2 + passed: true + comment: Labels correctly identify frequency points and r/x values. + - id: SC-06 + name: Title Format + score: 2 + max: 2 + passed: true + comment: 'Correct format: smith-chart-basic · bokeh · pyplots.ai' + data_quality: + score: 18 + max: 20 + items: + - id: DQ-01 + name: Feature Coverage + score: 7 + max: 8 + passed: true + comment: Shows impedance variation with frequency, resonance behavior, both + positive and negative reactance regions. Could show more variation in the + pattern. + - id: DQ-02 + name: Realistic Context + score: 7 + max: 7 + passed: true + comment: 'Excellent RF engineering scenario: antenna S11 sweep 1-6 GHz with + series RLC resonance model at 3.5 GHz, Q=5.' + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 5 + passed: true + comment: R=45-55Ω, X varies realistically. Z0=50Ω standard reference impedance. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Linear script: imports → constants → figure → grid drawing → data + generation → plotting → save.' + - id: CQ-02 + name: Reproducibility + score: 3 + max: 3 + passed: true + comment: np.random.seed(42) set for reproducibility. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: 'All imports used: numpy, bokeh.io, bokeh.models, bokeh.plotting.' + - id: CQ-04 + name: No Deprecated API + score: 1 + max: 1 + passed: true + comment: Uses current Bokeh API. + - id: CQ-05 + name: Output Correct + score: 1 + max: 1 + passed: true + comment: Saves as plot.png and plot.html. + library_features: + score: 3 + max: 5 + items: + - id: LF-01 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Uses ColumnDataSource, Label annotations, export_png, and saves interactive + HTML. Could utilize HoverTool for interactive impedance/frequency display. + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - annotations + - html-export + - patches + patterns: + - data-generation + - columndatasource + - iteration-over-groups + dataprep: + - normalization + styling: + - alpha-blending + - grid-styling