2025-11-23 20:43:12 +08:00
|
|
|
|
import numpy as np
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
import scipy.linalg as sl
|
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
from matplotlib.pyplot import MultipleLocator, FormatStrFormatter
|
|
|
|
|
|
from matplotlib.patches import Ellipse
|
|
|
|
|
|
import matplotlib.transforms as transforms
|
|
|
|
|
|
# import seaborn as sns
|
|
|
|
|
|
import os
|
2025-11-23 21:55:28 +08:00
|
|
|
|
import math
|
2025-11-23 20:43:12 +08:00
|
|
|
|
# plt.style.use('seaborn')
|
|
|
|
|
|
plt.rcParams['font.family'] = ['SimHei'] # 用来正常显示中文标签
|
|
|
|
|
|
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
|
|
|
|
|
|
pd.set_option('display.max_columns', None) #显示所有列,把行显示设置成最大
|
|
|
|
|
|
pd.set_option('display.max_rows', None) #显示所有行,把列显示设置成最大
|
2025-11-23 21:23:21 +08:00
|
|
|
|
#YDD
|
2025-11-23 20:43:12 +08:00
|
|
|
|
def die1234(data_calc,DieType):
|
|
|
|
|
|
"""
|
|
|
|
|
|
输入中科飞测量测结果及Die类型,输出4个Mark的量测结果,并与之前EVG量测数据保持一致
|
|
|
|
|
|
"""
|
|
|
|
|
|
Refer_EVG_data = data_calc[data_calc['Die type']==DieType]
|
|
|
|
|
|
Refer_EVG_data = Refer_EVG_data.pivot_table(values=['Misreg X','Misreg Y'],
|
|
|
|
|
|
index='Die NO.',
|
|
|
|
|
|
columns='Mark NO.').sort_index(axis=1,level=1)
|
|
|
|
|
|
Refer_EVG_data.columns = ['M1X','M1Y',
|
|
|
|
|
|
'M2X','M2Y',
|
|
|
|
|
|
'M3X','M3Y',
|
|
|
|
|
|
'M4X','M4Y',]
|
|
|
|
|
|
Refer_EVG_data.index.names = ["QX8800SP_Index"]
|
|
|
|
|
|
Refer_EVG_data.index = Refer_EVG_data.index.astype(int)
|
|
|
|
|
|
# 当前数据为Top坐标减去 Bottom坐标,除以-1000,与EVG保持一致
|
|
|
|
|
|
return Refer_EVG_data/-1000, describe_3s(Refer_EVG_data)
|
|
|
|
|
|
|
|
|
|
|
|
def Mis_T(df,DieType,UseMark):
|
|
|
|
|
|
"""
|
|
|
|
|
|
输入要进行误差分解的原始数据、Die类型以及使用的Mark个数,输出误差分解后的结果。
|
|
|
|
|
|
"""
|
|
|
|
|
|
# if Die == 'Die1': # 80um风车Mark坐标
|
|
|
|
|
|
# DieM1 = (-5008.778,6004.225)
|
|
|
|
|
|
# DieM2 = (5994.129,6252.542)
|
|
|
|
|
|
# DieM3 = (-5548.967,-6166.277)
|
|
|
|
|
|
# DieM4 = (5951.212,-6111.258)
|
|
|
|
|
|
# elif Die == 'Die2':
|
|
|
|
|
|
# DieM1 = (-5340.8,6295)
|
|
|
|
|
|
# DieM2 = (6170,6295)
|
|
|
|
|
|
# DieM3 = (-5450.8,-5995.8)
|
|
|
|
|
|
# DieM4 = (6140,-5954.8)
|
|
|
|
|
|
# elif Die == 'Die3':
|
|
|
|
|
|
# DieM1 = (-5014.16,5831.341)
|
|
|
|
|
|
# DieM2 = (5968.837,6100.804)
|
|
|
|
|
|
# DieM3 = (-5535,-6325)
|
|
|
|
|
|
# DieM4 = (5461.545,-6246.118)
|
|
|
|
|
|
# elif Die == 'Die4':
|
|
|
|
|
|
# DieM1 = (-5100.8,5945.8)
|
|
|
|
|
|
# DieM2 = (6170,5945.8)
|
|
|
|
|
|
# DieM3 = (-5450.8,-6345)
|
|
|
|
|
|
# DieM4 = (6170,-6345)
|
|
|
|
|
|
##EVG量测顺序
|
|
|
|
|
|
# if DieType == 'Die1': # 80um圆环Mark坐标
|
|
|
|
|
|
# DieM1 = (-5128.778,6004.225)
|
|
|
|
|
|
# DieM2 = (5874.13,6252.542)
|
|
|
|
|
|
# DieM3 = (-5668.967,-6166.277)
|
|
|
|
|
|
# DieM4 = (5831.21,-6111.258)
|
|
|
|
|
|
# elif DieType == 'Die2':
|
|
|
|
|
|
# DieM1 = (-5220.8,6295)
|
|
|
|
|
|
# DieM2 = (6050,6295)
|
|
|
|
|
|
# DieM3 = (-5570.8,-5995.8)
|
|
|
|
|
|
# DieM4 = (6020,-5954.8)
|
|
|
|
|
|
# elif DieType == 'Die3':
|
|
|
|
|
|
# DieM1 = (-5134.16,5831.341)
|
|
|
|
|
|
# DieM2 = (5848.84,6100.804)
|
|
|
|
|
|
# DieM3 = (-5655,-6325)
|
|
|
|
|
|
# DieM4 = (5341.55,-6246.118)
|
|
|
|
|
|
# elif DieType == 'Die4':
|
|
|
|
|
|
# DieM1 = (-5220.8,5945.8)
|
|
|
|
|
|
# DieM2 = (6050,5945.8)
|
|
|
|
|
|
# DieM3 = (-5570.8,-6345)
|
|
|
|
|
|
# DieM4 = (6050,-6345)
|
|
|
|
|
|
## ZKFC量测顺序
|
|
|
|
|
|
if DieType == 'Die1': # 80um圆环Mark坐标
|
|
|
|
|
|
DieM1 = (4808.778,5924.225)
|
|
|
|
|
|
DieM2 = (-5274.129,6217.542)
|
|
|
|
|
|
DieM3 = (5348.967,-6246.277)
|
|
|
|
|
|
DieM4 = (-6115.212,-6191.258)
|
|
|
|
|
|
elif DieType == 'Die2':
|
|
|
|
|
|
DieM1 = (5300.8,6215)
|
|
|
|
|
|
DieM2 = (-6210,6215)
|
|
|
|
|
|
DieM3 = (5410.8,-6075.8)
|
|
|
|
|
|
DieM4 = (-6210,-6025.8)
|
|
|
|
|
|
elif DieType == 'Die3':
|
|
|
|
|
|
DieM1 = (4814.16,5911.341)
|
|
|
|
|
|
DieM2 = (-6168.837,6180.804)
|
|
|
|
|
|
DieM3 = (5335,-6245)
|
|
|
|
|
|
DieM4 = (-5661.545,-6166.118)
|
|
|
|
|
|
elif DieType == 'Die4':
|
|
|
|
|
|
DieM1 = (5060.8,6025.8)
|
|
|
|
|
|
DieM2 = (-6210,6025.8)
|
|
|
|
|
|
DieM3 = (5410.8,-6265)
|
|
|
|
|
|
DieM4 = (-6210,-6265)
|
|
|
|
|
|
if UseMark == 2:
|
|
|
|
|
|
Mis_T = np.array([
|
|
|
|
|
|
[1,0,-DieM1[1],-DieM1[0]],
|
|
|
|
|
|
[0,1, DieM1[0],-DieM1[1]],
|
|
|
|
|
|
|
|
|
|
|
|
[1,0,-DieM4[1],-DieM4[0]],
|
|
|
|
|
|
[0,1, DieM4[0],-DieM4[1]],
|
|
|
|
|
|
])
|
|
|
|
|
|
EVG_T1 = np.linalg.inv(Mis_T)
|
|
|
|
|
|
T4 = np.dot(EVG_T1,df[['M1X','M1Y','M4X','M4Y']].T).T
|
|
|
|
|
|
elif UseMark == 4:
|
|
|
|
|
|
Mis_T = np.array([
|
|
|
|
|
|
[1,0,-DieM1[1],-DieM1[0]],
|
|
|
|
|
|
[0,1, DieM1[0],-DieM1[1]],
|
|
|
|
|
|
|
|
|
|
|
|
[1,0,-DieM2[1],-DieM2[0]],
|
|
|
|
|
|
[0,1, DieM2[0],-DieM2[1]],
|
|
|
|
|
|
|
|
|
|
|
|
[1,0,-DieM3[1],-DieM3[0]],
|
|
|
|
|
|
[0,1, DieM3[0],-DieM3[1]],
|
|
|
|
|
|
|
|
|
|
|
|
[1,0,-DieM4[1],-DieM4[0]],
|
|
|
|
|
|
[0,1, DieM4[0],-DieM4[1]],
|
|
|
|
|
|
])
|
|
|
|
|
|
EVG_T1 = np.linalg.pinv(Mis_T)
|
|
|
|
|
|
T4 = np.dot(EVG_T1,df.T).T
|
|
|
|
|
|
TG_D4 = pd.DataFrame(T4,index=df.index,columns=['TX','TY',r'$\theta$','R'])
|
|
|
|
|
|
TG_D4_copy = TG_D4.copy()
|
|
|
|
|
|
#EVG测量值MisAlignment分解后转到设备方向
|
|
|
|
|
|
TG_D4['TX'],TG_D4['TY']=TG_D4['TY'], -TG_D4['TX']
|
|
|
|
|
|
#角度值从弧度变为角度值
|
|
|
|
|
|
TG_D4[r'$\theta$'] = np.arcsin(TG_D4[r'$\theta$'])*180/np.pi
|
|
|
|
|
|
# runout缩放系数变更为实际长度
|
|
|
|
|
|
TG_D4['RL'] = TG_D4['R']*((DieM1[1]-DieM4[1])**2 + (DieM1[0]-DieM4[0])**2)**0.5/2
|
|
|
|
|
|
return TG_D4,TG_D4_copy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def Data_ETL(EVG,Die_Type,Mark_Type):
|
|
|
|
|
|
df_EVG = EVG.sort_index().copy()
|
|
|
|
|
|
df_EVG = df_EVG[df_EVG.applymap(isnumber)].astype(np.float64)
|
|
|
|
|
|
df_EVG_new = pd.DataFrame()
|
|
|
|
|
|
df_EVG_new["X"] = df_EVG.iloc[:,1]
|
|
|
|
|
|
df_EVG_new["Y"] = -df_EVG.iloc[:,0]
|
|
|
|
|
|
df_EVG_new["Die_Type"] = Die_Type
|
|
|
|
|
|
df_EVG_new["Mark_Type"] = Mark_Type
|
|
|
|
|
|
df_EVG_new = df_EVG_new.rename_axis("EVG_Index")
|
|
|
|
|
|
return df_EVG_new
|
|
|
|
|
|
|
|
|
|
|
|
def Data_ETL_Cat(folder,path,*dfs):
|
|
|
|
|
|
df = pd.concat([i for i in dfs])
|
|
|
|
|
|
df.to_excel(folder+path+'.xlsx')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_html_SP_1(path):
|
|
|
|
|
|
"""
|
|
|
|
|
|
读取QX8800SP第 1 批EVG测量html文件数据。
|
|
|
|
|
|
文件夹内有两个html,一个是Mark1,另一个是Mark4
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 指定文件夹路径
|
|
|
|
|
|
folder_path = path
|
|
|
|
|
|
|
|
|
|
|
|
# 获取文件夹中的所有文件名
|
|
|
|
|
|
file_names = os.listdir(folder_path)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建一个字典来存储读取的Excel文件
|
|
|
|
|
|
html_data = {'M1':[],'M2':[],'M4':[],'M3':[],}
|
|
|
|
|
|
|
|
|
|
|
|
# 逐个读取Excel文件
|
|
|
|
|
|
for file_name in file_names:
|
|
|
|
|
|
# 检查文件扩展名是否为Excel文件
|
|
|
|
|
|
if file_name.endswith('.html'):
|
|
|
|
|
|
# 使用Pandas读取Excel文件
|
|
|
|
|
|
file_path = os.path.join(folder_path, file_name)
|
|
|
|
|
|
df = pd.read_html(file_path)
|
|
|
|
|
|
try:
|
|
|
|
|
|
if ('MARK1' in df[0].loc[3][1]):
|
|
|
|
|
|
df[2]['Identifier'] = np.array([i[6:] for i in df[2]['Identifier']], dtype=int)
|
|
|
|
|
|
M1 = df[2].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M1.columns = ["M1X","M1Y"]
|
|
|
|
|
|
M1.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M1'] = M1
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK2' in df[0].loc[3][1]):
|
|
|
|
|
|
df[2]['Identifier'] = np.array([i[6:] for i in df[2]['Identifier']], dtype=int)
|
|
|
|
|
|
M2 = df[2].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M2.columns = ["M2X","M2Y"]
|
|
|
|
|
|
M2.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M3'] = M2
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK3' in df[0].loc[3][1]):
|
|
|
|
|
|
df[2]['Identifier'] = np.array([i[6:] for i in df[2]['Identifier']], dtype=int)
|
|
|
|
|
|
M3 = df[2].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M3.columns = ["M3X","M3Y"]
|
|
|
|
|
|
M3.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M3'] = M3
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK4' in df[0].loc[3][1]):
|
|
|
|
|
|
df[2]['Identifier'] = np.array([i[6:] for i in df[2]['Identifier']], dtype=int)
|
|
|
|
|
|
M4 = df[2].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M4.columns = ["M4X","M4Y"]
|
|
|
|
|
|
M4.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M4'] = M4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
|
continue
|
|
|
|
|
|
return html_data
|
|
|
|
|
|
|
|
|
|
|
|
def read_html_SP_2(path):
|
|
|
|
|
|
"""
|
|
|
|
|
|
读取QX8800SP第 2 批EVG测量html文件数据。
|
|
|
|
|
|
文件夹内有两个html,一个是Mark1,另一个是Mark4
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 指定文件夹路径
|
|
|
|
|
|
folder_path = path
|
|
|
|
|
|
|
|
|
|
|
|
# 获取文件夹中的所有文件名
|
|
|
|
|
|
file_names = os.listdir(folder_path)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建一个字典来存储读取的Excel文件
|
|
|
|
|
|
html_data = {'M1':[],'M2':[],'M3':[],'M4':[]}
|
|
|
|
|
|
|
|
|
|
|
|
# 逐个读取Excel文件
|
|
|
|
|
|
for file_name in file_names:
|
|
|
|
|
|
# 检查文件扩展名是否为Excel文件
|
|
|
|
|
|
if file_name.endswith('.html'):
|
|
|
|
|
|
# 使用Pandas读取Excel文件
|
|
|
|
|
|
file_path = os.path.join(folder_path, file_name)
|
|
|
|
|
|
df = pd.read_html(file_path)
|
|
|
|
|
|
try:
|
|
|
|
|
|
if ('MARK1' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M1 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M1.columns = ["M1X","M1Y"]
|
|
|
|
|
|
M1.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M1'] = M1
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK2' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M2 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M2.columns = ["M2X","M2Y"]
|
|
|
|
|
|
M2.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M2'] = M2
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK3' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M3 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M3.columns = ["M3X","M3Y"]
|
|
|
|
|
|
M3.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M3'] = M3
|
|
|
|
|
|
|
|
|
|
|
|
elif ('MARK4' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M4 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
M4.columns = ["M4X","M4Y"]
|
|
|
|
|
|
M4.index.names = ["EVG"]
|
|
|
|
|
|
html_data['M4'] = M4
|
|
|
|
|
|
except:
|
|
|
|
|
|
continue
|
|
|
|
|
|
return html_data
|
|
|
|
|
|
def read_html_SP_3(path):
|
|
|
|
|
|
"""
|
|
|
|
|
|
读取QX8800SP第 2 批EVG测量html文件数据。
|
|
|
|
|
|
文件夹内有两个html,一个是Mark1,另一个是Mark4
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 指定文件夹路径
|
|
|
|
|
|
folder_path = path
|
|
|
|
|
|
|
|
|
|
|
|
# 获取文件夹中的所有文件名
|
|
|
|
|
|
file_names = os.listdir(folder_path)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建一个字典来存储读取的Excel文件
|
|
|
|
|
|
html_data = {'M1':[],'M2':[],'M3':[]}
|
|
|
|
|
|
|
|
|
|
|
|
# 逐个读取Excel文件
|
|
|
|
|
|
for file_name in file_names:
|
|
|
|
|
|
# 检查文件扩展名是否为Excel文件
|
|
|
|
|
|
if file_name.endswith('.html'):
|
|
|
|
|
|
# 使用Pandas读取Excel文件
|
|
|
|
|
|
file_path = os.path.join(folder_path, file_name)
|
|
|
|
|
|
df = pd.read_html(file_path)
|
|
|
|
|
|
try:
|
|
|
|
|
|
if ('MARK1' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[3:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M1 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
ID = df[0].loc[0][1]
|
|
|
|
|
|
file_name = ID + 'M1'
|
|
|
|
|
|
html_data['M1'] = M1
|
|
|
|
|
|
elif ('MARK4' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M2 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
ID = df[0].loc[0][1]
|
|
|
|
|
|
file_name = ID + 'M2'
|
|
|
|
|
|
html_data['M2'] = M2
|
|
|
|
|
|
elif ('MARK3' in df[0].loc[3][1]):
|
|
|
|
|
|
df[4]['Identifier'] = np.array([i[6:] for i in df[4]['Identifier']], dtype=int)
|
|
|
|
|
|
M3 = df[4].iloc[::-1,[0,5,6]].set_index('Identifier')
|
|
|
|
|
|
html_data['M3'] = M3
|
|
|
|
|
|
except:
|
|
|
|
|
|
continue
|
|
|
|
|
|
return html_data
|
|
|
|
|
|
|
|
|
|
|
|
def describe_3s(df):
|
|
|
|
|
|
des_df = df.describe()
|
|
|
|
|
|
des_df.loc["range"] = des_df.loc['max']-des_df.loc['min']
|
|
|
|
|
|
des_df.loc["3sigma"] = des_df.loc['std']*3
|
|
|
|
|
|
return des_df
|
|
|
|
|
|
|
|
|
|
|
|
def result_show(df):
|
|
|
|
|
|
df = describe_3s(df)
|
|
|
|
|
|
df_show = df[['TX','TY','$\\theta$','R']].loc[['mean','3sigma']]
|
|
|
|
|
|
df_show['TX'],df_show['TY'] = df_show['TX']*1000,df_show['TY']*1000
|
|
|
|
|
|
df_show['$\\theta$'] = df_show['$\\theta$']*np.pi/180*1000000
|
|
|
|
|
|
df_show['R'] = df_show['R']*1000000
|
|
|
|
|
|
df_show.columns = ['TX(nm)','TY(nm)','theta(μrad)','R(ppm)']
|
|
|
|
|
|
return df_show.T
|
|
|
|
|
|
|
|
|
|
|
|
def isnumber(x):
|
|
|
|
|
|
try:
|
|
|
|
|
|
float(x)
|
|
|
|
|
|
return True
|
|
|
|
|
|
except:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def calc_theta(M1X,M1Y,M2X,M2Y):
|
|
|
|
|
|
w = 11545.5
|
|
|
|
|
|
h = 11739
|
|
|
|
|
|
w2 = w - M1X + M2X
|
|
|
|
|
|
h2 = h - M1Y + M2Y
|
|
|
|
|
|
a1 = np.arctan2(h,w)*180/np.pi
|
|
|
|
|
|
a2 = np.arctan2(h2,w2)*180/np.pi
|
|
|
|
|
|
return a2-a1
|
|
|
|
|
|
def calc_center(M1X,M1Y,M2X,M2Y):
|
|
|
|
|
|
MCX = (M2X-M1X)/2+M1X
|
|
|
|
|
|
MCY = (M2Y-M1Y)/2+M1Y
|
|
|
|
|
|
return MCX,MCY
|
|
|
|
|
|
def angle2offset(angle):
|
|
|
|
|
|
return 2*np.sin(angle/2*np.pi/180)*16841.364
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Create a plot of the covariance confidence ellipse of *x* and *y*.
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
x, y : array-like, shape (n, )
|
|
|
|
|
|
Input data.
|
|
|
|
|
|
ax : matplotlib.axes.Axes
|
|
|
|
|
|
The axes object to draw the ellipse into.
|
|
|
|
|
|
n_std : float
|
|
|
|
|
|
The number of standard deviations to determine the ellipse's radiuses.
|
|
|
|
|
|
**kwargs
|
|
|
|
|
|
Forwarded to `~matplotlib.patches.Ellipse`
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
matplotlib.patches.Ellipse
|
|
|
|
|
|
"""
|
|
|
|
|
|
if x.size != y.size:
|
|
|
|
|
|
raise ValueError("x and y must be the same size")
|
|
|
|
|
|
|
|
|
|
|
|
cov = np.cov(x, y)
|
|
|
|
|
|
pearson = cov[0, 1]/np.sqrt(cov[0, 0] * cov[1, 1])
|
|
|
|
|
|
# Using a special case to obtain the eigenvalues of this
|
|
|
|
|
|
# two-dimensional dataset.
|
|
|
|
|
|
ell_radius_x = np.sqrt(1 + pearson)
|
|
|
|
|
|
ell_radius_y = np.sqrt(1 - pearson)
|
|
|
|
|
|
ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2,
|
|
|
|
|
|
facecolor=facecolor, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
# Calculating the standard deviation of x from
|
|
|
|
|
|
# the squareroot of the variance and multiplying
|
|
|
|
|
|
# with the given number of standard deviations.
|
|
|
|
|
|
scale_x = np.sqrt(cov[0, 0]) * n_std
|
|
|
|
|
|
mean_x = np.mean(x)
|
|
|
|
|
|
|
|
|
|
|
|
# calculating the standard deviation of y ...
|
|
|
|
|
|
scale_y = np.sqrt(cov[1, 1]) * n_std
|
|
|
|
|
|
mean_y = np.mean(y)
|
|
|
|
|
|
|
|
|
|
|
|
transf = transforms.Affine2D() \
|
|
|
|
|
|
.rotate_deg(45) \
|
|
|
|
|
|
.scale(scale_x, scale_y) \
|
|
|
|
|
|
.translate(mean_x, mean_y)
|
|
|
|
|
|
|
|
|
|
|
|
ellipse.set_transform(transf + ax.transData)
|
|
|
|
|
|
return ax.add_patch(ellipse)
|
|
|
|
|
|
|
|
|
|
|
|
def plt_3sigma(data_calc,Data_ID):
|
|
|
|
|
|
theta = np.linspace(0, 2*np.pi, 100)
|
|
|
|
|
|
|
|
|
|
|
|
radius1 = 0.533
|
|
|
|
|
|
a1 = radius1*np.cos(theta)
|
|
|
|
|
|
b1 = radius1*np.sin(theta)
|
|
|
|
|
|
|
|
|
|
|
|
radius2 = 1.067
|
|
|
|
|
|
a2 = radius2*np.cos(theta)
|
|
|
|
|
|
b2 = radius2*np.sin(theta)
|
|
|
|
|
|
|
|
|
|
|
|
radius3 = 1.6
|
|
|
|
|
|
a3 = radius3*np.cos(theta)
|
|
|
|
|
|
b3 = radius3*np.sin(theta)
|
|
|
|
|
|
fig, ax_nstd = plt.subplots(1,2)
|
|
|
|
|
|
|
|
|
|
|
|
ax_nstd[0].axvline(c='grey', lw=1)
|
|
|
|
|
|
ax_nstd[0].axhline(c='grey', lw=1)
|
|
|
|
|
|
|
|
|
|
|
|
x1, y1 = data_calc['M1X'].dropna().values,data_calc['M1Y'].dropna().values
|
|
|
|
|
|
ax_nstd[0].scatter(x1, y1, s=3)
|
|
|
|
|
|
for i in data_calc.dropna().index:
|
|
|
|
|
|
ax_nstd[0].annotate(i, xy=(data_calc.loc[i,"M1X"],data_calc.loc[i,"M1Y"]),
|
|
|
|
|
|
xytext=(data_calc.loc[i,"M1X"],data_calc.loc[i,"M1Y"]),
|
|
|
|
|
|
color="k")
|
|
|
|
|
|
ax_nstd[0].plot(a3,b3,color='r',label=r'$3\sigma$ 1.6um')
|
|
|
|
|
|
ax_nstd[0].plot(a2,b2,color='b',label=r'$2\sigma$ 1.067um')
|
|
|
|
|
|
ax_nstd[0].plot(a1,b1,color='g',label=r'$1\sigma$ 0.533um')
|
|
|
|
|
|
|
|
|
|
|
|
confidence_ellipse(x1, y1, ax_nstd[0], n_std=1, linewidth=1.5,
|
|
|
|
|
|
label=r'$1\sigma$', edgecolor='firebrick')
|
|
|
|
|
|
confidence_ellipse(x1, y1, ax_nstd[0], n_std=2, linewidth=2,
|
|
|
|
|
|
label=r'$2\sigma$', edgecolor='fuchsia', linestyle='--')
|
|
|
|
|
|
confidence_ellipse(x1, y1, ax_nstd[0], n_std=3, linewidth=2,
|
|
|
|
|
|
label=r'$3\sigma$', edgecolor='orange', linestyle='-.')
|
|
|
|
|
|
ax_nstd[0].set_xlim((-3,3))
|
|
|
|
|
|
ax_nstd[0].set_ylim((-3,3))
|
|
|
|
|
|
ax_nstd[0].set_title(fr'{Data_ID} Die1 Mark1 $3\sigma$分布')
|
|
|
|
|
|
ax_nstd[0].legend()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ax_nstd[1].axvline(c='grey', lw=1)
|
|
|
|
|
|
ax_nstd[1].axhline(c='grey', lw=1)
|
|
|
|
|
|
|
|
|
|
|
|
x2, y2 = data_calc['M4X'].dropna().values,data_calc['M4Y'].dropna().values
|
|
|
|
|
|
ax_nstd[1].scatter(x2, y2, s=3)
|
|
|
|
|
|
|
|
|
|
|
|
for i in data_calc.dropna().index:
|
|
|
|
|
|
ax_nstd[1].annotate(i, xy=(data_calc.loc[i,"M4X"],data_calc.loc[i,"M4Y"]),
|
|
|
|
|
|
xytext=(data_calc.loc[i,"M4X"],data_calc.loc[i,"M4Y"]),
|
|
|
|
|
|
color="k")
|
|
|
|
|
|
ax_nstd[1].plot(a3,b3,color='r',label=r'$3\sigma$ 1.6um')
|
|
|
|
|
|
ax_nstd[1].plot(a2,b2,color='b',label=r'$2\sigma$ 1.067um')
|
|
|
|
|
|
ax_nstd[1].plot(a1,b1,color='g',label=r'$1\sigma$ 0.533um')
|
|
|
|
|
|
|
|
|
|
|
|
confidence_ellipse(x2, y2, ax_nstd[1], n_std=1, linewidth=1.5,
|
|
|
|
|
|
label=r'$1\sigma$', edgecolor='firebrick')
|
|
|
|
|
|
confidence_ellipse(x2, y2, ax_nstd[1], n_std=2, linewidth=2,
|
|
|
|
|
|
label=r'$2\sigma$', edgecolor='fuchsia', linestyle='--')
|
|
|
|
|
|
confidence_ellipse(x2, y2, ax_nstd[1], n_std=3, linewidth=2,
|
|
|
|
|
|
label=r'$3\sigma$', edgecolor='orange', linestyle='-.')
|
|
|
|
|
|
ax_nstd[1].set_xlim((-3,3))
|
|
|
|
|
|
ax_nstd[1].set_ylim((-3,3))
|
|
|
|
|
|
ax_nstd[1].set_title(fr'{Data_ID} Mark4 $3\sigma$分布')
|
|
|
|
|
|
ax_nstd[1].legend()
|
|
|
|
|
|
|
|
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
def plt_data(plt_data,name):
|
|
|
|
|
|
fig, ax = plt.subplots(3,3)
|
|
|
|
|
|
keys = [i for i in plt_data]
|
|
|
|
|
|
#plot_X = plt_data.index[1]
|
|
|
|
|
|
ax[0][0].plot(plt_data.index, plt_data[keys[0]], linestyle = '-', # 折线类型
|
|
|
|
|
|
linewidth = 1, color = 'steelblue', # 折线颜色
|
|
|
|
|
|
marker = 'o', markersize = 5, # 点的形状大小
|
|
|
|
|
|
markeredgecolor='black', # 点的边框色
|
|
|
|
|
|
markerfacecolor='brown') # 点的填充色
|
|
|
|
|
|
ax[0][0].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[0][0].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[0][0].set_title(keys[0])
|
|
|
|
|
|
|
|
|
|
|
|
ax[0][1].plot(plt_data.index, plt_data[keys[2]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='b')
|
|
|
|
|
|
ax[0][1].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[0][1].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[0][1].set_title(keys[2])
|
|
|
|
|
|
|
|
|
|
|
|
ax[0][2].plot(plt_data.index, plt_data[keys[6]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='m')
|
|
|
|
|
|
ax[0][2].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[0][2].yaxis.set_major_locator(MultipleLocator(0.0015))
|
|
|
|
|
|
ax[0][2].set_title('偏转角度')
|
|
|
|
|
|
|
|
|
|
|
|
ax[1][0].plot(plt_data.index, plt_data[keys[1]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='m')
|
|
|
|
|
|
ax[1][0].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[1][0].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[1][0].set_title(keys[1])
|
|
|
|
|
|
|
|
|
|
|
|
ax[1][1].plot(plt_data.index, plt_data[keys[3]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='g')
|
|
|
|
|
|
ax[1][1].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[1][1].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[1][1].set_title(keys[3])
|
|
|
|
|
|
|
|
|
|
|
|
ax[1][2].plot(plt_data.index, plt_data['MCX'], linestyle = '-', linewidth = 1, color = 'steelblue', label='MCX',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='r')
|
|
|
|
|
|
ax[1][2].plot(plt_data.index, plt_data['MCY'], linestyle = '-', linewidth = 1, color = 'steelblue', label='MCY',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='y')
|
|
|
|
|
|
ax[1][2].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[1][2].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[1][2].set_title('Mark中心偏移XY')
|
|
|
|
|
|
ax[1][2].legend()
|
|
|
|
|
|
|
|
|
|
|
|
ax[2][0].plot(plt_data.index, plt_data[keys[-3]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='m')
|
|
|
|
|
|
ax[2][0].axhline(1.6,c='orange',ls='-.',label='Length:1.6um')
|
|
|
|
|
|
ax[2][0].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[2][0].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[2][0].set_title(keys[-3])
|
|
|
|
|
|
ax[2][0].legend()
|
|
|
|
|
|
|
|
|
|
|
|
ax[2][1].plot(plt_data.index, plt_data[keys[-2]], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='m')
|
|
|
|
|
|
ax[2][1].axhline(1.6,c='orange',ls='-.',label='Length:1.6um')
|
|
|
|
|
|
ax[2][1].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[2][1].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[2][1].set_title(keys[-2])
|
|
|
|
|
|
ax[2][1].legend()
|
|
|
|
|
|
|
|
|
|
|
|
ax[2][2].plot(plt_data.index, plt_data['MCL'], linestyle = '-', linewidth = 1, color = 'steelblue',
|
|
|
|
|
|
marker = 'o', markersize = 5, markeredgecolor='black', markerfacecolor='m')
|
|
|
|
|
|
ax[2][2].axhline(1.6,c='orange',ls='-.',label='Length:1.6um')
|
|
|
|
|
|
ax[2][2].xaxis.set_major_locator(MultipleLocator(5))
|
|
|
|
|
|
ax[2][2].yaxis.set_major_locator(MultipleLocator(0.5))
|
|
|
|
|
|
ax[2][2].set_title('Mark中心偏移MCL')
|
|
|
|
|
|
ax[2][2].legend()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plt.suptitle(f'{name}:贴片数据')
|
|
|
|
|
|
fig.tight_layout()
|
|
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def wafer_data_make(data_list, gap1, new_origin):
|
|
|
|
|
|
# 该函数用于设置wafer位置信息
|
|
|
|
|
|
'''
|
|
|
|
|
|
:param data_list: 如['mark1','mark2','mark3','mark4']
|
|
|
|
|
|
:param gap1: die位置间隔设置,即每个矩形的间隔设置
|
|
|
|
|
|
:param new_origin: 原点位置设定
|
|
|
|
|
|
:return:
|
|
|
|
|
|
'''
|
|
|
|
|
|
df_empty = pd.DataFrame(columns=['芯片号', 'P_X', 'P_Y'])
|
|
|
|
|
|
Die_count = 0
|
|
|
|
|
|
# i的最大值即为列数
|
|
|
|
|
|
for i in range(len(data_list)):
|
|
|
|
|
|
# j的最大值即为该列的Die个数
|
|
|
|
|
|
for j in range(int(data_list[i][2])):
|
|
|
|
|
|
x_coordinate = (int(data_list[i][0]) - float(new_origin[0])) * gap1
|
|
|
|
|
|
y_coordinate = (-int(data_list[i][1]) - j + float(new_origin[1])) * gap1
|
|
|
|
|
|
Die_count = Die_count + 1
|
|
|
|
|
|
df_empty.loc[Die_count - 1] = [Die_count, x_coordinate, y_coordinate]
|
|
|
|
|
|
df_empty['芯片号'] = df_empty['芯片号'].astype(int)
|
|
|
|
|
|
wafer_data = df_empty.set_index('芯片号')
|
|
|
|
|
|
return wafer_data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def plt_Vmap(V_data,data0,name):
|
|
|
|
|
|
plt.figure()
|
|
|
|
|
|
L1 = V_data["M1L"]
|
|
|
|
|
|
L2 = V_data["M4L"]
|
|
|
|
|
|
gap = 150
|
|
|
|
|
|
gap_rec = 400
|
|
|
|
|
|
plt.quiver(V_data['P_X'] - gap, V_data['P_Y'] + gap,
|
|
|
|
|
|
V_data['M1X'], V_data['M1Y'],
|
|
|
|
|
|
V_data["M1L"], cmap='coolwarm', units='xy',width = 40) # gap(自定义): 定义箭头的起始位置(4个Mark点的位置)
|
|
|
|
|
|
plt.quiver(V_data["P_X"] + gap, V_data["P_Y"] - gap,
|
|
|
|
|
|
V_data["M4X"], V_data["M4Y"],
|
|
|
|
|
|
V_data["M4L"], cmap="coolwarm", units='xy',width = 40)
|
|
|
|
|
|
for i in data0.index:
|
|
|
|
|
|
color = 'k'
|
|
|
|
|
|
plt.annotate(i, xy=(data0.loc[i,"P_X"],data0.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(data0.loc[i,"P_X"]-50,data0.loc[i,"P_Y"]-50),
|
|
|
|
|
|
color=color)
|
|
|
|
|
|
|
|
|
|
|
|
rectangle = plt.Rectangle(xy=(data0.loc[i, 'P_X'] - gap_rec/2, data0.loc[i, 'P_Y'] - gap_rec/2), width=gap_rec,
|
|
|
|
|
|
height=gap_rec, fill=False, color='k')
|
|
|
|
|
|
plt.gca().add_patch(rectangle)
|
|
|
|
|
|
|
|
|
|
|
|
# if np.isnan(L1[i]) or np.isnan(L2[i]):
|
|
|
|
|
|
# color = 'k'
|
|
|
|
|
|
# elif L1[i]<1.6 and L2[i]<1.6:
|
|
|
|
|
|
# color = 'b'
|
|
|
|
|
|
# else:
|
|
|
|
|
|
# color = 'r'
|
|
|
|
|
|
# #中心点偏移
|
|
|
|
|
|
# plt.annotate(r"$C$:" + str(round(V_data["MCL"][i],2)),
|
|
|
|
|
|
# xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
# xytext=(V_data.loc[i,"P_X"]-200,V_data.loc[i,"P_Y"]+100),
|
|
|
|
|
|
# color=color)
|
|
|
|
|
|
# #右Mark偏移
|
|
|
|
|
|
# plt.annotate(r"$R$:" + str(round(L2[i],3)),
|
|
|
|
|
|
# xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
# xytext=(V_data.loc[i,"P_X"]+100,V_data.loc[i,"P_Y"]+100),
|
|
|
|
|
|
# color=color)
|
|
|
|
|
|
# #左Mark偏移
|
|
|
|
|
|
# plt.annotate(r"$L$:" + str(round(L1[i],3)),
|
|
|
|
|
|
# xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
# xytext=(V_data.loc[i,"P_X"]-200,V_data.loc[i,"P_Y"]-100),
|
|
|
|
|
|
# color=color)
|
|
|
|
|
|
# plt.annotate(r"$\theta$:" + str(round(V_data.loc[i,"M_R_14"],4)),
|
|
|
|
|
|
# xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
# xytext=(V_data.loc[i,"P_X"]+100,V_data.loc[i,"P_Y"]-100),
|
|
|
|
|
|
# color=color)
|
|
|
|
|
|
# plt.xlim(-3000,2500)
|
|
|
|
|
|
# plt.ylim(-3000,3000)
|
|
|
|
|
|
plt.colorbar()
|
|
|
|
|
|
plt.title(f'{name}:C:中心点偏移,L(左下):Mark1偏移,R(右上):Mark2偏移,(单位:um)')
|
|
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def plt_XYmap(V_data,name):
|
|
|
|
|
|
plt.figure()
|
|
|
|
|
|
L1 = V_data["M1L"]
|
|
|
|
|
|
L2 = V_data["M4L"]
|
|
|
|
|
|
plt.quiver(V_data["P_X"],V_data["P_Y"],V_data["MCX"],V_data["MCY"],
|
|
|
|
|
|
V_data["MCL"], cmap="coolwarm", units='xy')
|
|
|
|
|
|
def plt_annotate(V_data,i,color):
|
|
|
|
|
|
#中心点偏移
|
|
|
|
|
|
plt.annotate(r"$X$:" + str(round(V_data["MCX"][i],2)),
|
|
|
|
|
|
xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(V_data.loc[i,"P_X"]-200,V_data.loc[i,"P_Y"]+50),
|
|
|
|
|
|
color=color)
|
|
|
|
|
|
#右Mark偏移
|
|
|
|
|
|
plt.annotate(r"$C$:" + str(round(V_data["MCL"][i],2)),
|
|
|
|
|
|
xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(V_data.loc[i,"P_X"]+50,V_data.loc[i,"P_Y"]+50),
|
|
|
|
|
|
color=color)
|
|
|
|
|
|
#左Mark偏移
|
|
|
|
|
|
plt.annotate(r"$Y$:" + str(round(V_data["MCY"][i],2)),
|
|
|
|
|
|
xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(V_data.loc[i,"P_X"]-200,V_data.loc[i,"P_Y"]-50),
|
|
|
|
|
|
color=color)
|
|
|
|
|
|
#角度偏移
|
|
|
|
|
|
plt.annotate(r"$\theta$:" + str(round(V_data.loc[i,"M_R"],4)),
|
|
|
|
|
|
xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(V_data.loc[i,"P_X"]+50,V_data.loc[i,"P_Y"]-50),
|
|
|
|
|
|
color=color)
|
|
|
|
|
|
for i in V_data.index:
|
|
|
|
|
|
plt.annotate(i, xy=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
xytext=(V_data.loc[i,"P_X"],V_data.loc[i,"P_Y"]),
|
|
|
|
|
|
color="k")
|
|
|
|
|
|
if np.isnan(L1[i]) or np.isnan(L2[i]):
|
|
|
|
|
|
plt_annotate(V_data,i,'k')
|
|
|
|
|
|
|
|
|
|
|
|
elif L1[i]<=0.707 and L2[i]<=0.707:
|
|
|
|
|
|
plt_annotate(V_data,i,'orange')
|
|
|
|
|
|
|
|
|
|
|
|
elif L1[i]<=1.13 and L2[i]<=1.13:
|
|
|
|
|
|
plt_annotate(V_data,i,'g')
|
|
|
|
|
|
|
|
|
|
|
|
elif L1[i]<=1.6 and L2[i]<=1.6:
|
|
|
|
|
|
plt_annotate(V_data,i,'b')
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
plt_annotate(V_data,i,'r')
|
|
|
|
|
|
|
|
|
|
|
|
# plt.xlim(-2500,2500)
|
|
|
|
|
|
# plt.ylim(-3000,3000)
|
|
|
|
|
|
plt.colorbar()
|
|
|
|
|
|
plt.title(f'{name}:C:中心点偏移矢量,X:中心点X偏移,Y:中心点Y偏移,(单位:um)')
|
|
|
|
|
|
plt.show()
|