Skip to content

Instantly share code, notes, and snippets.

@jbcrail
Last active December 30, 2015 18:20
Show Gist options
  • Save jbcrail/4f8fd19b6c0c285a4d3c to your computer and use it in GitHub Desktop.
Save jbcrail/4f8fd19b6c0c285a4d3c to your computer and use it in GitHub Desktop.
Yield Analysis for Silicon Wafer
import numpy as np
import pandas as pd
from collections import OrderedDict
from bokeh.models import ColumnDataSource, GridPlot, HoverTool
from bokeh.palettes import RdYlGn9
from bokeh.plotting import Figure, hplot, vplot, output_file, show
def generate_dataset(name, size):
"""
Create a square dataset of values between [0, 1.0) drawn from a distribution
based on the given name.
"""
if name == "cluster":
# Cluster of error values
a = np.rint(np.random.multivariate_normal([size/4, 3*size/4], [[1.2, 0], [0.4, 0.8]], size=10000))
df = pd.DataFrame(np.vstack(a), columns=['x', 'y'])
df['yield'] = np.random.random_sample((10000,))
cluster = df.groupby(['x', 'y']).mean().reset_index()
# Sample of good values to be merged into
b = np.ones((size, size))
df = pd.DataFrame(b).unstack().reset_index()
df.columns = ['x', 'y', 'yield']
df = pd.merge(df, cluster, how='left', on=['x', 'y'])
df['yield'] = np.where(df.yield_y.isnull(), df.yield_x, df.yield_y)
df = df.drop('yield_x', axis=1)
df = df.drop('yield_y', axis=1)
else:
# Uniformly random sample of error values
grid = pd.DataFrame(np.random.random_sample((size, size)))
df = grid.unstack().reset_index()
df.columns = ['x', 'y', 'yield']
return df
def apply_palette(df, palette):
# Create a color mapping
edges = np.linspace(0.0, 1.0, num=len(palette)+1)[1:]
color_map = dict(zip(edges, list(reversed(palette))))
def categorize_bins(n):
for upper in sorted(color_map.keys()):
if n <= upper:
return color_map[upper]
return "NaN"
df['color'] = df['yield'].map(categorize_bins)
return df
def minimum_grid(n, default):
return default if n < default else n
def contains_point(center, radius, x, y):
return (x-center[0])**2 + (y-center[1])**2 < radius**2
def make_plot(name, title, size):
def contains_square(row):
center = ((size-1)/2.0, (size-1)/2.0)
radius = size / 2.0
corners = [(row.x+0.5, row.y+0.5), (row.x+0.5, row.y-0.5), (row.x-0.5, row.y-0.5), (row.x-0.5, row.y+0.5)]
return all([contains_point(center, radius, p[0], p[1]) for p in corners])
df = generate_dataset(name, size)
df = apply_palette(df, RdYlGn9)
df['percentage'] = df['yield'].map(lambda n: n * 100)
df['contains'] = df.apply(contains_square, axis=1)
df = df[df.contains]
source = ColumnDataSource(df)
min_grid_size = minimum_grid(size * 10, 600)
p = Figure(
plot_width=min_grid_size,
plot_height=min_grid_size,
x_range=(-1, size),
y_range=(-1, size),
tools="hover,save",
toolbar_location=None)
p.circle(x=(size-1)/2.0, y=(size-1)/2.0, radius=size/2.0, line_color='#bbbbbb', fill_color='#bbbbbb')
p.rect('x', 'y', name='dies', source=source, color='color', width=0.9, height=0.9)
p.title = title
p.axis.visible = None
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.outline_line_color = None
hover = p.select(dict(type=HoverTool))
hover.names = ["dies"]
hover.tooltips = OrderedDict([
("Yield", "@percentage%"),
])
return p
if __name__ == '__main__':
# Approximate size chart for wafers:
# - 6" (150mm) => 12
# - 8" (200mm) => 16
# - 12" (300mm) => 25
plot_6_random = make_plot("random", "6\" wafer w/ random errors", 12)
plot_6_cluster = make_plot("cluster", "6\" wafer w/ clustered errors", 12)
plot_8_random = make_plot("random", "8\" wafer w/ random errors", 16)
plot_8_cluster = make_plot("cluster", "8\" wafer w/ clustered errors", 16)
plot_12_random = make_plot("random", "12\" wafer w/ random errors", 25)
plot_12_cluster = make_plot("cluster", "12\" wafer w/ clustered errors", 25)
layout = vplot(
hplot(plot_6_random, plot_6_cluster),
hplot(plot_8_random, plot_8_cluster),
hplot(plot_12_random, plot_12_cluster)
)
output_file("wafer_yield.html", title="Wafer Yield Example")
show(layout)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment