你好,
我是 jmp 的新手(目前使用 jmp 16)并且对以下场景有疑问。 我有一个 csv 文件,每个缺陷都有以下列:
- 时间戳、序列号、x 位置、y 位置、缺陷大小(以毫米为单位)
我在清洁过程中工作,检查工具会提供当天每一个缺陷的信息。 我需要跟踪几件事:
1) 处理特定序列 ID 的次数,并确定它是第一次被清理,还是第二次、第三次等。
2) 如果序列 ID 被清洗了不止一次,那么我需要知道每次清洗后 (x,y) 位置的任何组合是否仍然在同一个位置
绘制 x 和 y 位置图,然后按时间过滤我可以得到答案 2),但这是一个非常手动的步骤。 我希望找到一个使用 jsl 的解决方案,它可以自动化我需要跟踪的信息。
我喜欢克雷格黑尔斯解决方案,但我也想提供我的解决方案,因为我过去曾面临过类似的任务。
我的方法是使用可用数据计算附加信息,将其添加到数据表中,然后我能够生成显示最终结果的报告(图形生成器、表格等)。
在您的请求中,您希望能够计算特定序列号的处理次数。我还发现创建一个指示器列非常有用,该指示器列可识别来自晶圆的最后/最终处理的数据条目。
基于来自“mock data.PNG”的示例数据,我将执行以下操作:
- 计算每次处理晶圆的顺序号
- 计算每个晶圆的最终处理时间
- 根据大致位置按名称识别缺陷位置
- 计算每个晶圆的每个位置名称被识别的次数
为此,我使用制表法生成了几个临时数据表,添加了一些公式以获得我想要的答案,然后将它们合并回原始数据表。
JSL 代码看起来像这样
names default to here(1);
/////////// make example data table /////////////////////
dt = New Table( "example data set",
Add Rows( 13 ),
New Column( "Time",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4] )
),
New Column( "WaferID",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 1, 1, 1] )
),
New Column( "X location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[2.745, 1.566, -28.143, -4.793, -29.251, 19.527, 6.581, 29.37, 4.907,
21.699, 2.745, -4.793, 6.581]
)
),
New Column( "Y location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[44.164, 61.915, -42.954, -38.091, -46.659, 32.201, -24.715, 41.533,
60.217, -60.26, 44.164, -38.091, -24.715]
)
),
New Column( "Size",
Numeric,
"Continuous",
Format( "Best", 12 ),
Set Values(
[0.164, 0.136, 0.125, 0.122, 0.122, 0.036, 0.035, 0.035, 0.035, 0.034,
0.164, 0.122, 0.035]
)
)
);
//dt = current data table();
/////////// create temporary data table to determine Clean Count and Final Clean /////////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :WaferID, :Time ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << New Column("Wafer Clean Count", Numeric, "Nominal", Formula(Col Rank(:Time, :WaferID)));
dtx << New Column("Final Wafer Clean", Numeric, "Nominal", Formula(If(:Time == Col Maximum(:Time, :WaferID), 1, 0)));
dtx << delete Column("N");
///////// move Clean Count and Final Clean information back to original data table //////////////
dt << Update(
With( dtx ),
Match Columns( :WaferID = :WaferID, :Time = :Time ),
Add Columns from Update Table( :Wafer Clean Count, :Final Wafer Clean ),
Replace Columns in Main Table( :Wafer Clean Count, :Final Wafer Clean )
);
//////// close data table //////////////////////////////////////////////////////
close(dtx, no save);
//////// modify X & Y values to allow variation in measurements////////////////
dt << new column("X_Temp", numeric, continuous, formula(round(:X location, 1)));
dt << new column("Y_Temp", numeric, continuous, formula(round(:Y location, 1)));
/////// create temporary data table to name defect locations ///////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :X_Temp, :Y_Temp ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << new column("Location", character, nominal, formula("L" || char(row())));
dtx << delete column("N");
//////// move Location information back to original data table ////////////////
dt << Update(
With( dtx ),
Match Columns( :X_Temp = :X_Temp, :Y_Temp = :Y_Temp ),
Add Columns from Update Table( :Location ),
Replace Columns in Main Table( :Location )
);
/////// clean up /////////////////////////
close(dtx, no save);
dt << delete columns(
{
"X_Temp",
"Y_Temp"
}
);
/////// calculate the number of times a defect location occured on a given wafer ////////
dt << new column("Wafer Location Count", numeric, nominal, formula(Col Rank( :Time, :Location, :WaferID )));
/////// make some reports /////////////
nw = new window("results",
h list box(
gb = dt << Graph Builder(
Show Control Panel( 0 ),
Variables(
X( :X location ),
Y( :Y location ),
Group X( :Wafer Clean Count ),
Group Y( :WaferID ),
Color( :Wafer Location Count )
),
Elements( Points( X, Y, Legend( 7 ) ) )
),
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table(
Column Table( Statistics( Max ), Analysis Columns( :Wafer Location Count ) ),
Row Table( Grouping Columns( :WaferID, :X location, :Y location ) )
),
Local Data Filter(
Add Filter(
columns( :Final Wafer Clean ),
Where( :Final Wafer Clean == 1 )
)
)
)
)
);在命名缺陷位置时,我将 x 坐标和 y 坐标四舍五入到小数点后一位,以允许位置测量值发生一些变化。 这可能会或可能不会满足您的需求,但这种方法允许您修改 X_Temp 和 Y_Temp 的公式,其余的仍将按预期工作。
生成的数据表包含以下附加列:
- 晶圆清洁计数
- 最终晶圆清洁
- 如果条目不是最后一次处理晶圆,则指示器列为零,如果是最后一次处理晶圆,则为一
- 地点
- 晶圆位置计数
使用生成的数据表,可以解决您提出的问题等等。 可以调查的一些可能问题包括但不限于:
- 多个清洁周期后哪些缺陷位置仍然存在
- 第一次晶圆清洗周期后的整体缺陷率是多少
- 执行所有清洁周期后的总体缺陷率是多少
- 每个晶圆的平均清洁周期数是多少
- 是否有区域始终存在需要清洁的缺陷区域
我希望这有帮助。
这是一些入门代码。 您需要对其进行研究以确保它符合您的预期。 输出在日志窗口中。 (我会附上一个文件。下面的代码可能会搞砸,因为该网站删除了一些“无效的 html”。 )
// synthesize some data
radius = 1;
dt = New Table( "Untitled",
Add Rows( 1000 ), // <<<<<<<<<<<<<< change number of rows here
New Column( "time", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "waferID", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "xLocation", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "yLocation", Numeric, "Continuous", Format( "Best", 12 ) )
);
here:time = 0; // for this synth data, time just increments by 1
here:waferID = 0; // and the waferID is random 0-9
For( row = 1, row <= N Rows( dt ), row += 1,
If( Random Uniform() > .8, // determines how many points in a cleaning, before time changes and wafer re-randomizes
here:time += 1;
here:waferID = Random Integer( 0, 9 );
);
dt:time[row] = here:time;
dt:waferID[row] = here:waferID;
dt:xLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
dt:yLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
);
// find the unique waferIDs using summary
dtWafer = dt << Summary( Group( :waferID ), Link to original data table( 1 ) );
dtWaferTime = dt << Summary( Group( :waferID, :time ) );
dtNWafer = dtWaferTime << Summary( Group( :waferID ) ); //number of distinct times for this wafer is number of cleanings
close = .03; // <<< this determines how close makes a hit
// produce a report for each waferID that shows up in the cleaning data...
For( iWafer = 1, iWafer <= N Rows( dtWafer ), iWafer += 1,
dtWafer << clear select << selectrows( iWafer ); // dtWafer selection also selects rows in dt because linked
rows = dt << get selected rows; // all the rows for this wafer, across all times
// rows is a 1-column matrix of row numbers in dt for this wafer. the argument to KDTable
// is all the locations over all cleanings for this wafer.
kdt = KDTable( dt[rows, {xLocation, yLocation}] );
// report header for each waferID... the report might have no lines below the header, or a bunch... Not indented.
Write( Eval Insert( "\!n\!nprocessing waferID=^dtWafer:waferID[iWafer]^, ^nrows(rows)^ points during ^dtNWafer:nrows[iWafer]^ cleanings" ) );
For( irow = 1, irow <= N Rows( rows ), irow += 1, // the rows should be ascending time
row = rows[irow]; // row (singular) is an index to dt
// find rows {up to this many,up to this far} near irow
{index, distance} = kdt << kNearestRows( {dtWafer:NRows[iWafer], close}, irow );
// above, index is into the 1-column matrix of row numbers. below, it becomes index into dt
index = rows[index];
printedHeader = 0; // don't print this header unless there are duplicates to go with it
// distance[] and index[] have same number of rows; index is indexes to near neighbors and distance is to the neighbor
For( idistance = 1, idistance <= N Items( distance ), idistance += 1,
If( dt:waferID[index[idistance]] != dt:waferID[row] | dt:waferID[row] != dtWafer:waferID[iWafer],
Throw( "wafer mismatch" )
);// sanity check
If( distance[idistance] <= close, // typically there is one item with a bigger distance than we care about, must be ignored by else-break below.
If( !printedHeader, // a little indented...
Write(
Eval Insert(
"\!n matches for row ^row^ time ^dt:time[row]^ loc [^char(dt:xLocation[row],7,3)^,^char(dt:yLocation[row],7,3)^]"
)
);
printedHeader = 1;
);
// there can be more than one subsequent cleaning, indented more...
Write( Eval Insert( "\!n row ^index[idistance]^ time ^dt:time[index[idistance]]^ " ) );
Write( Eval Insert( "[^char(dt:xLocation[index[idistance]],7,3)^,^char(dt:yLocation[index[idistance]],7,3)^]) " ) );
Write( Eval Insert( "dist(^distance[idistance]^)" ) );
, //else
Break()
);
);
kdt << removerows( irow ); // never look back at this row from future rows
);
);
// a graph to examine the rows found to be close.
dt << Graph Builder(
Size( 867, 787 ),
Show Control Panel( 0 ),
Variables( X( :xLocation ), Y( :yLocation ) ),
Elements( Points( X, Y, Legend( 5 ) ) ),
Local Data Filter( Add Filter( columns( :waferID ), Modeling Type( :waferID, Nominal ), Display( :waferID, N Items( 14 ) ) ) )
);
x,y 位置有什么样的分辨率? 例如,它们可能始终是从 1 到 100 的整数,或者它们可能是从 0 到 100 的浮点值,四舍五入到最接近的 .01,或者它们可能是没有明显分辨率限制的浮点数。 x,y 对必须有多接近才能重复? 缺陷的大小与 x,y 重叠有什么关系吗? 或者尺寸总是小于 x,y 网格并被忽略?
如果只有 100 个 x 和 100 个 y 值,并且大小不感兴趣,那么我会将 x 和 y 值组合成一个键,也许是 x*100+y,这样可以更容易地测试重复. 圆形浮点数可能相同,最多约 6 位有效数字。
但如果大小很重要,或者必须测试距离,我可能会使用 KDTable。
还有,多少行?少于几千个可能不需要任何特定的优化。 超过一百万可能需要一个深思熟虑的算法。 (在一千行以下,每一行都可以考虑每隔一行,只有 1,000,000 次比较,但大小加倍,比较四倍。)