diff --git a/plots/smith-chart-basic/implementations/plotnine.py b/plots/smith-chart-basic/implementations/plotnine.py new file mode 100644 index 0000000000..c3af89c4b7 --- /dev/null +++ b/plots/smith-chart-basic/implementations/plotnine.py @@ -0,0 +1,158 @@ +""" pyplots.ai +smith-chart-basic: Smith Chart for RF/Impedance +Library: plotnine 0.15.2 | Python 3.13.11 +Quality: 90/100 | Created: 2026-01-15 +""" + +import numpy as np +import pandas as pd +from plotnine import ( + aes, + annotate, + coord_fixed, + element_blank, + element_rect, + element_text, + geom_path, + geom_point, + geom_text, + ggplot, + labs, + scale_x_continuous, + scale_y_continuous, + theme, +) + + +# Reference impedance +Z0 = 50 + +# Generate Smith chart grid - constant resistance circles +# For resistance r, circle center at (r/(r+1), 0) with radius 1/(r+1) +r_values = [0, 0.2, 0.5, 1, 2, 5] +theta = np.linspace(0, 2 * np.pi, 200) + +r_circle_data = [] +for r in r_values: + cx = r / (r + 1) + radius = 1 / (r + 1) + x = cx + radius * np.cos(theta) + y = radius * np.sin(theta) + # Clip to unit circle + mask = x**2 + y**2 <= 1.001 + r_circle_data.append(pd.DataFrame({"x": x[mask], "y": y[mask], "type": "r_circle", "value": f"r={r}"})) + +r_circles_df = pd.concat(r_circle_data, ignore_index=True) + +# Constant reactance arcs +# For reactance x, circle center at (1, 1/x) with radius 1/|x| +x_values = [0.2, 0.5, 1, 2, 5] +reactance_data = [] + +for xv in x_values: + # Positive reactance (upper half) + radius_x = 1 / abs(xv) + cy_pos = 1 / xv + t = np.linspace(0, 2 * np.pi, 500) + arc_x = 1 + radius_x * np.cos(t) + arc_y = cy_pos + radius_x * np.sin(t) + # Keep only points inside unit circle + mask = (arc_x**2 + arc_y**2 <= 1.001) & (arc_x >= -0.01) + if np.any(mask): + reactance_data.append(pd.DataFrame({"x": arc_x[mask], "y": arc_y[mask], "type": "x_arc", "value": f"x={xv}"})) + + # Negative reactance (lower half) + cy_neg = -1 / xv + arc_y_neg = cy_neg + radius_x * np.sin(t) + mask_neg = (arc_x**2 + arc_y_neg**2 <= 1.001) & (arc_x >= -0.01) + if np.any(mask_neg): + reactance_data.append( + pd.DataFrame({"x": arc_x[mask_neg], "y": arc_y_neg[mask_neg], "type": "x_arc", "value": f"x=-{xv}"}) + ) + +reactance_df = pd.concat(reactance_data, ignore_index=True) + +# Outer boundary circle (|gamma| = 1) +boundary_theta = np.linspace(0, 2 * np.pi, 200) +boundary_df = pd.DataFrame( + {"x": np.cos(boundary_theta), "y": np.sin(boundary_theta), "type": "boundary", "value": "boundary"} +) + +# Horizontal axis (real axis) +axis_df = pd.DataFrame({"x": [-1, 1], "y": [0, 0], "type": "axis", "value": "axis"}) + +# Example impedance data: antenna S11 measurement from 1-6 GHz +np.random.seed(42) +n_points = 50 +freq_ghz = np.linspace(1, 6, n_points) + +# Simulate antenna impedance variation with frequency +# Creates realistic spiral pattern on Smith chart +z_real = 50 + 30 * np.sin(2 * np.pi * freq_ghz / 2.5) + np.random.randn(n_points) * 3 +z_imag = 20 * np.cos(2 * np.pi * freq_ghz / 3) + 15 * (freq_ghz - 3) + np.random.randn(n_points) * 2 + +# Normalize impedance +z_norm = (z_real + 1j * z_imag) / Z0 + +# Convert to reflection coefficient (gamma) for Smith chart coordinates +gamma = (z_norm - 1) / (z_norm + 1) +gamma_real = np.real(gamma) +gamma_imag = np.imag(gamma) + +impedance_df = pd.DataFrame({"x": gamma_real, "y": gamma_imag, "freq": freq_ghz, "type": "impedance"}) + +# Select frequency labels at key points +label_indices = [0, 12, 24, 36, 49] +labels_df = impedance_df.iloc[label_indices].copy() +labels_df["label"] = [f"{f:.1f} GHz" for f in labels_df["freq"]] +labels_df["y_offset"] = labels_df["y"] + 0.08 + +# Combine grid data +grid_df = pd.concat([r_circles_df, reactance_df, boundary_df, axis_df], ignore_index=True) + +# Create plot +plot = ( + ggplot() + # Grid lines + + geom_path( + aes(x="x", y="y", group="value"), + data=grid_df[grid_df["type"] == "r_circle"], + color="#CCCCCC", + size=0.5, + alpha=0.8, + ) + + geom_path( + aes(x="x", y="y", group="value"), data=grid_df[grid_df["type"] == "x_arc"], color="#CCCCCC", size=0.5, alpha=0.8 + ) + # Boundary circle + + geom_path(aes(x="x", y="y"), data=boundary_df, color="#333333", size=1) + # Real axis + + geom_path(aes(x="x", y="y"), data=axis_df, color="#333333", size=0.5) + # Impedance locus curve + + geom_path(aes(x="x", y="y"), data=impedance_df, color="#306998", size=1.5) + # Data points + + geom_point(aes(x="x", y="y"), data=impedance_df, color="#306998", size=2, alpha=0.7) + # Frequency labels + + geom_text(aes(x="x", y="y_offset", label="label"), data=labels_df, color="#FFD43B", size=12, fontweight="bold") + # Center point marker (Z = Z0) + + annotate("point", x=0, y=0, color="#E74C3C", size=4) + + annotate("text", x=0.12, y=-0.08, label="Z=Z₀", color="#E74C3C", size=11) + # Styling + + coord_fixed(ratio=1, xlim=(-1.3, 1.3), ylim=(-1.3, 1.3)) + + scale_x_continuous(breaks=[]) + + scale_y_continuous(breaks=[]) + + labs(title="smith-chart-basic · plotnine · pyplots.ai", x="", y="") + + theme( + figure_size=(12, 12), + plot_title=element_text(size=24, ha="center", weight="bold"), + panel_background=element_rect(fill="white"), + panel_grid_major=element_blank(), + panel_grid_minor=element_blank(), + axis_text=element_blank(), + axis_ticks=element_blank(), + plot_background=element_rect(fill="white"), + ) +) + +# Save +plot.save("plot.png", dpi=300) diff --git a/plots/smith-chart-basic/metadata/plotnine.yaml b/plots/smith-chart-basic/metadata/plotnine.yaml new file mode 100644 index 0000000000..7231e60cc5 --- /dev/null +++ b/plots/smith-chart-basic/metadata/plotnine.yaml @@ -0,0 +1,218 @@ +library: plotnine +specification_id: smith-chart-basic +created: '2026-01-15T21:51:40Z' +updated: '2026-01-15T21:54:21Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 21047489075 +issue: 3792 +python_version: 3.13.11 +library_version: 0.15.2 +preview_url: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/plotnine/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/smith-chart-basic/plotnine/plot_thumb.png +preview_html: null +quality_score: 90 +review: + strengths: + - Excellent implementation of Smith chart using plotnine grammar of graphics approach + - Mathematically correct calculation of constant resistance circles and reactance + arcs + - Proper conversion from complex impedance to reflection coefficient coordinates + - Clean layered composition with grid, boundary, axis, data locus, and annotations + - Realistic RF engineering data scenario with antenna S11 measurements + - Good use of coord_fixed for maintaining circular aspect ratio + weaknesses: + - Frequency labels in yellow may have lower contrast against white background than + ideal + - Grid line labels (r=0, r=0.2, x=0.5, etc.) not shown - would enhance educational + value + - Data point markers could be slightly larger for better visibility at full resolution + image_description: The plot displays a Smith chart with the correct circular layout. + The outer boundary is a black circle representing |gamma|=1 (total reflection). + Inside, light gray constant resistance circles are drawn (centered along the horizontal + axis) and constant reactance arcs curve from the right edge. A horizontal real + axis line passes through the center. The impedance locus is drawn as a connected + blue (#306998) path with small markers showing an antenna S11 measurement sweep + from 1-6 GHz. Five frequency labels (1.0 GHz, 2.2 GHz, 3.4 GHz, 4.7 GHz, 6.0 GHz) + appear in yellow/gold text positioned above their corresponding data points. A + red dot marks the center point (Z=Z₀) with a red label. The chart uses a clean + white background with no axis labels or tick marks (appropriate for a Smith chart). + The title "smith-chart-basic · plotnine · pyplots.ai" appears centered at the + top in bold black text. + criteria_checklist: + visual_quality: + score: 36 + max: 40 + items: + - id: VQ-01 + name: Text Legibility + score: 9 + max: 10 + passed: true + comment: Title is large and bold, frequency labels are readable though could + be slightly larger + - id: VQ-02 + name: No Overlap + score: 8 + max: 8 + passed: true + comment: No overlapping text elements + - id: VQ-03 + name: Element Visibility + score: 7 + max: 8 + passed: true + comment: Grid lines visible with appropriate alpha, data points could be slightly + larger + - id: VQ-04 + name: Color Accessibility + score: 5 + max: 5 + passed: true + comment: Blue/yellow/red color scheme is colorblind-safe with good contrast + - id: VQ-05 + name: Layout Balance + score: 5 + max: 5 + passed: true + comment: Chart is well-centered with balanced margins, uses square 1:1 aspect + ratio appropriately + - id: VQ-06 + name: Axis Labels + score: 0 + max: 2 + passed: true + comment: N/A for Smith chart (no conventional axes), but spec-appropriate + - id: VQ-07 + name: Grid & Legend + score: 2 + max: 2 + passed: true + comment: Grid lines are subtle with appropriate alpha, no legend needed + spec_compliance: + score: 24 + max: 25 + items: + - id: SC-01 + name: Plot Type + score: 8 + max: 8 + passed: true + comment: Correct Smith chart with resistance circles and reactance arcs + - id: SC-02 + name: Data Mapping + score: 5 + max: 5 + passed: true + comment: Complex impedance correctly converted to reflection coefficient (gamma) + coordinates + - id: SC-03 + name: Required Features + score: 5 + max: 5 + passed: true + comment: 'All required features present: grid, normalized impedance, connected + locus curve, frequency labels, center marker' + - id: SC-04 + name: Data Range + score: 3 + max: 3 + passed: true + comment: All data visible within unit circle boundary + - id: SC-05 + name: Legend Accuracy + score: 1 + max: 2 + passed: true + comment: No legend needed, but labels could better explain the grid values + - id: SC-06 + name: Title Format + score: 2 + max: 2 + passed: true + comment: 'Correct format: smith-chart-basic · plotnine · 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 creating realistic spiral + pattern, visits different regions of Smith chart + - id: DQ-02 + name: Realistic Context + score: 7 + max: 7 + passed: true + comment: Antenna S11 measurement from 1-6 GHz is a realistic RF engineering + scenario + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 5 + passed: true + comment: Impedance values around 50 ohms reference are realistic, frequency + range is typical for antenna design + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Flat structure: imports, data generation, plot construction, save' + - id: CQ-02 + name: Reproducibility + score: 3 + max: 3 + passed: true + comment: Uses np.random.seed(42) + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports are used + - id: CQ-04 + name: No Deprecated API + score: 1 + max: 1 + passed: true + comment: Uses current plotnine API + - id: CQ-05 + name: Output Correct + score: 0 + max: 1 + passed: false + comment: 'Minor: could use higher dpi for 4800x2700 target' + library_features: + score: 3 + max: 5 + items: + - id: LF-01 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Uses plotnine grammar of graphics with layered geom_path and geom_point, + coord_fixed for aspect ratio, proper theming + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - annotations + - layer-composition + - patches + patterns: + - data-generation + - iteration-over-groups + - matrix-construction + dataprep: + - normalization + styling: + - minimal-chrome + - alpha-blending