@wafertools/wafermap 0.29.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +26 -17
- package/CHANGELOG.md +410 -0
- package/README.md +6 -6
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
- package/dist/packages/canvas-adapter/charts/trend.js +1 -1
- package/dist/packages/canvas-adapter/deprecated.d.ts +7 -0
- package/dist/packages/canvas-adapter/deprecated.js +1 -0
- package/dist/packages/canvas-adapter/dieList.d.ts +1 -3
- package/dist/packages/canvas-adapter/dieList.js +7 -7
- package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
- package/dist/packages/canvas-adapter/exportName.js +1 -0
- package/dist/packages/canvas-adapter/icons.js +2 -2
- package/dist/packages/canvas-adapter/index.d.ts +2 -3
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/insightsTab.d.ts +0 -1
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +23 -40
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +96 -104
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +31 -13
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.d.ts +20 -12
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +6 -0
- package/dist/packages/canvas-adapter/toolbar.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +26 -15
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/warnings.js +1 -1
- package/dist/packages/core/deprecated.d.ts +60 -0
- package/dist/packages/core/deprecated.js +1 -0
- package/dist/packages/core/index.d.ts +1 -3
- package/dist/packages/core/index.js +1 -1
- package/dist/packages/core/passBins.d.ts +33 -0
- package/dist/packages/core/passBins.js +1 -0
- package/dist/packages/core/utils.d.ts +12 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/core/waferLabel.d.ts +35 -0
- package/dist/packages/core/waferLabel.js +1 -0
- package/dist/packages/renderer/binColors.d.ts +105 -13
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +8 -9
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +76 -17
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/colorMap.d.ts +4 -3
- package/dist/packages/renderer/colorSchemes.d.ts +8 -6
- package/dist/packages/renderer/deprecate.d.ts +31 -0
- package/dist/packages/renderer/deprecate.js +1 -0
- package/dist/packages/renderer/deprecated.d.ts +45 -0
- package/dist/packages/renderer/deprecated.js +1 -0
- package/dist/packages/renderer/index.d.ts +4 -4
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +6 -1
- package/dist/packages/stats/capability.d.ts +16 -1
- package/dist/packages/stats/capability.js +1 -1
- package/dist/packages/stats/clusterDetection.d.ts +0 -2
- package/dist/packages/stats/deprecated.d.ts +82 -0
- package/dist/packages/stats/deprecated.js +1 -0
- package/dist/packages/stats/index.d.ts +5 -14
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/normalizeInput.d.ts +10 -0
- package/dist/packages/stats/normalizeInput.js +1 -0
- package/dist/packages/stats/regions.d.ts +9 -2
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderFindingsReport.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +42 -1
- package/dist/packages/stats/renderSummaryReport.js +34 -34
- package/dist/packages/stats/reportHtml.d.ts +0 -1
- package/dist/packages/stats/reportHtml.js +10 -10
- package/dist/packages/stats/summaryFigures.d.ts +47 -0
- package/dist/packages/stats/summaryFigures.js +1 -0
- package/dist/packages/stats/types.d.ts +118 -43
- package/dist/packages/stats/yield.d.ts +12 -6
- package/dist/packages/stats/yield.js +1 -1
- package/package.json +1 -1
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import{isParametricTest as
|
|
2
|
-
<td class="tight">${
|
|
3
|
-
<td class="tight">${
|
|
4
|
-
<td>${
|
|
5
|
-
<td class="numeric">${
|
|
6
|
-
<td class="numeric">${
|
|
1
|
+
import{waferDisplayLabel as L}from"../core/waferLabel.js";import{binPassSets as we,binPassSetsByWafer as $e,mergeBinDefs as I}from"../renderer/binColors.js";import{itemPassBins as R,passBinsLabel as xe}from"../core/passBins.js";import{isParametricTest as De}from"../renderer/buildWaferMap.js";import{buildRingRegions as j,buildQuadrantRegions as O,buildRegionYieldData as ve}from"./regions.js";import{openHtmlReport as Be}from"./renderFindingsReport.js";import{analyzeWaferLot as Te}from"./analyzeWaferLot.js";import{computeFunctionalYield as Fe,analyzeWaferMap as Pe}from"./analyzeWaferMap.js";import{mergeTestDefs as Ye}from"./mergeTestDefs.js";import{sortBinsForDisplay as z}from"./binPareto.js";import{buildFacetTable as We,facetValueOf as Me,FACET_NONE_VALUE as Ce}from"./facets.js";import{visibleFindings as A}from"./filterFindings.js";import{buildYieldDataCombined as Re}from"./yield.js";import{describeWaferPopulation as G,populationLabel as Ne}from"./population.js";import{buildTestPassRateData as Ee,hasJudgeableTests as ke,poolFunctionalYield as He}from"./testPassRate.js";import{buildCapabilityData as Le}from"./capability.js";import{fmt as F}from"../renderer/fmt.js";import{getDieKey as Ie,isPositionedDie as je,diePassStatus as U}from"../core/dies.js";import{formatFindingDelta as Oe,formatFindingCoverage as ze,formatFindingTooltip as Ae,renderMetadataSection as V,renderMetricGrid as _,renderSection as x,renderSeverityBadge as Ge,renderTable as B,reportStyles as N}from"./reportHtml.js";import{escHtml as h}from"../core/utils.js";function C(t,o){return o===0?"\u2014":`${(t/o*100).toFixed(1)}%`}function Ue(t){if(!t)return"";const o=t.lot??t.lotId,s=t.wafer??t.waferId,n=[o,s].filter(Boolean).map(String);return n.length?` \u2014 ${n.map(h).join(" \xB7 ")}`:""}function K(t,o,s,n,r=[1]){const e=new Map;if(n)for(const[a,c]of Object.entries(n))e.set(Number(a),c);else for(const a of t){if(a.partial||a.edgeExcluded)continue;const c=s==="hard"?a.hbin:a.sbin;c!=null&&e.set(c,(e.get(c)??0)+1)}if(!e.size)return"";const i=[...e.values()].reduce((a,c)=>a+c,0),l=o?new Map(o.map(a=>[a.bin,a])):null,d=z(e.entries(),we(t,r)[s]).map(([a,c])=>{const p=l?.get(a)?.name??"\u2014";return[String(a),p,String(c),C(c,i)]});return x(s==="hard"?"Hard Bin Breakdown":"Soft Bin Breakdown",B(["Bin","Name","Count","%"],d,{className:"compact"}))}function Q(t,o,s,n){const r=new Set(n),e=new Map(s.map(d=>[Ie(d),d]));if(!s.some(d=>d.hbin!=null||d.sbin!=null)||!o.length)return"";const l=[];for(const d of o){let f=0,a=0;for(const c of d.dieKeys){const b=e.get(c);if(!b||b.partial||b.edgeExcluded)continue;const p=U(b,r);p!==void 0&&(a++,p&&f++)}a&&l.push([d.label,String(f),String(a),C(f,a)])}return l.length?x(t,B(["Region","Pass","Total","Yield"],l,{className:"compact"})):""}function q(t,o,s){if(!o.length)return"";const n=t.filter(i=>!i.partial&&!i.edgeExcluded),r=new Map((s??[]).map(i=>[i.testNumber,i])),e=[];for(const i of o.filter(De)){const l=i.testNumber,d=i.unit||void 0,f=r.get(l);let a,c,b;if(f)({min:a,max:c,mean:b}=f);else{const p=n.map(w=>w.testValues?.[l]).filter(w=>w!==void 0&&isFinite(w));if(!p.length)continue;p.sort((w,y)=>w-y),a=p[0],c=p[p.length-1],b=p.reduce((w,y)=>w+y,0)/p.length}e.push([h(i.name),F(a,d),F(b,d),F(c,d)])}return e.length?x("Test Values",B(["Test","Min","Mean","Max"],e,{className:"compact"})):""}function J(t,o,s){const n=s??Fe(t,o);if(!n?.length)return"";const r=n.map(e=>[h(e.label),String(e.totalDies),String(e.passDies),String(e.failDies),e.passRatePercent!==null?`${e.passRatePercent.toFixed(1)}%`:"\u2014"]);return x("Functional Tests",B(["Test","N","Pass","Fail","Pass Rate"],r,{className:"compact"}))}function X(t,o){const s=Le(t,o);if(!s.length)return"";const n=e=>e===null?"\u2014":e.toFixed(2),r=s.map(e=>[h(e.label),e.hasSpec?`${F(e.lsl,e.unit)} \u2013 ${F(e.usl,e.unit)}`:"\u2014",F(e.mean,e.unit),n(e.cp),n(e.cpk),n(e.pp),n(e.ppk)]);return x("Process Capability",B(["Test","Spec (LSL\u2013USL)","Mean","Cp","Cpk","Pp","Ppk"],r,{className:"compact"}))}function Z(t,o){const s=t.map(r=>`<tr title="${h(Ae(r))}">
|
|
2
|
+
<td class="tight">${Ge(r.severity)}</td>
|
|
3
|
+
<td class="tight">${h(r.comparison.left)}</td>
|
|
4
|
+
<td>${h(r.variable.label)}</td>
|
|
5
|
+
<td class="numeric">${h(Oe(r))}</td>
|
|
6
|
+
<td class="numeric">${h(ze(r,o))}</td>
|
|
7
7
|
</tr>`).join(`
|
|
8
8
|
`);return`<table class="report-table findings-table compact"><thead><tr>
|
|
9
|
-
<th>Severity</th><th>Region</th><th>Metric</th><th class="numeric">Delta</th><th class="numeric">${
|
|
10
|
-
</tr></thead><tbody>${
|
|
11
|
-
`)),
|
|
9
|
+
<th>Severity</th><th>Region</th><th>Metric</th><th class="numeric">Delta</th><th class="numeric">${o!==void 0?"Wafers":"N (region/rest)"}</th>
|
|
10
|
+
</tr></thead><tbody>${s}</tbody></table>`}function ee(t,o){const s=A(t);return s.length?x("Findings",Z(s,o)):""}export function renderSummaryReportHtml(t,o={}){const{wafer:s,dies:n,yieldSummary:r,dataCoverage:e,hbinDefs:i,sbinDefs:l,testDefs:d=[],statsSummary:f,passBins:a=[1],ringCount:c=4}=t,b=s.metadata,p=Ue(b),w=o.title??`Wafer Summary${p}`,y=new Date().toLocaleString(),u=n.some(Y=>Y.hbin!=null),g=n.some(Y=>Y.sbin!=null),S=n.filter(je),D=j(S,s,c),$=O(S,s,c),v=[{label:"Total dies",value:String(r.totalDies)},{label:"Filled dies",value:String(e.filledDies),hint:`${C(e.filledDies,e.totalDies)} fill`},{label:"Pass dies",value:String(r.passDies)},{label:"Fail dies",value:String(r.failDies)},...r.partialDies>0?[{label:"Partial",value:String(r.partialDies)}]:[],...r.yieldPercent!==null?[{label:`Yield (pass: ${xe([a])})`,value:`${r.yieldPercent.toFixed(1)}%`}]:[],...r.edgeExcludedDies>0?[{label:"Edge excluded (outer zone)",value:String(r.edgeExcludedDies)}]:[]],P=[V([{metadata:s.metadata}]),x("Summary",[_(v)].filter(Boolean).join(`
|
|
11
|
+
`)),u?K(n,i,"hard",f?.stats.hardBinCounts,a):g?K(n,l,"soft",f?.stats.softBinCounts,a):"",Q("Ring Yield",D,n,a),Q("Quadrant Yield",$,n,a),q(n,d,f?.stats.perTestStats),J(n,d,f?.stats.functionalYield),X([{dies:n}],d),f?ee(f.findings):""].filter(Boolean).join(`
|
|
12
12
|
`);return`<!DOCTYPE html>
|
|
13
13
|
<html lang="en">
|
|
14
14
|
<head>
|
|
15
15
|
<meta charset="UTF-8">
|
|
16
|
-
<title>${
|
|
16
|
+
<title>${h(w)}</title>
|
|
17
17
|
<style>
|
|
18
|
-
${
|
|
18
|
+
${N()}
|
|
19
19
|
</style>
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
|
22
22
|
<main class="report">
|
|
23
23
|
<header class="report-header">
|
|
24
|
-
<h1>${
|
|
25
|
-
<p class="report-subtitle">Generated ${
|
|
24
|
+
<h1>${h(w)}</h1>
|
|
25
|
+
<p class="report-subtitle">Generated ${h(y)}</p>
|
|
26
26
|
</header>
|
|
27
|
-
${
|
|
28
|
-
<p class="footer">Generated ${
|
|
27
|
+
${P}
|
|
28
|
+
<p class="footer">Generated ${h(y)}</p>
|
|
29
29
|
</main>
|
|
30
30
|
</body>
|
|
31
|
-
</html>`}function
|
|
32
|
-
`))}const
|
|
33
|
-
`)}`)}if(!
|
|
34
|
-
`)+
|
|
35
|
-
`);return{lotSummary:
|
|
31
|
+
</html>`}function Ve(t,o){const s=t.perWafer.map(n=>{const r=L(o[n.waferIndex],n.waferIndex),e=n.summary.stats.yieldPercent;return[r,e!==null?`${e.toFixed(1)}%`:"N/A"]});return B(["Wafer","Yield"],s,{className:"compact"})}function _e(t){const o=[];for(const e of t.perWafer){const i=A(e.summary.findings);if(!i.length)continue;const l=e.summary.wafer?.waferId??`Wafer ${e.waferIndex+1}`;o.push(`<h3 class="report-subheading">${h(l)}</h3>`+Z(i))}if(!o.length)return"";const s=o.length,n=t.perWafer.length,r=`<p class="report-note">${s} of ${n} wafer${n===1?"":"s"} have per-wafer findings; the rest had none.</p>`;return x("Findings by Wafer",r+o.join(`
|
|
32
|
+
`))}const Ke=3;function Qe(t,o,s){const n=t.map(d=>({metadata:d.wafer?.metadata})),r=We(n,{facetableOnly:!0}).filter(d=>d.splittable);if(!r.length)return"";const e=r.slice(0,Ke),i=[];for(const d of e){const f=new Map;t.forEach((u,g)=>{const S=Me(u.wafer?.metadata,d.key)??Ce;(f.get(S)??f.set(S,[]).get(S)).push(g)});const a=[...f.entries()].map(([u,g])=>({key:u,items:g.map(S=>t[S])}));if(a.length<2)continue;const c=a.map(u=>[h(u.key),h(u.items.map(g=>g.label).join(", "))]),b=Re(a.map(u=>({key:u.key,items:u.items.map(g=>({label:g.label,dies:g.dies,passBins:R(g,s)}))})),s).map(u=>[h(u.label),String(u.itemCount),`${u.percent.toFixed(1)}%`]),p=[B([d.label,"Wafers"],c,{className:"compact"}),B([d.label,"Wafers","Yield"],b,{className:"compact"})],w=t.flatMap(u=>u.dies??[]);let y=0;for(const u of["spec","testFlag","functional"]){if(!ke(o,u,w))continue;const g=Ee(a.map($=>({key:$.key,items:$.items.map(v=>({dies:v.dies,testSpecYield:v.statsSummary?.stats.testSpecYield,functionalYield:v.statsSummary?.stats.functionalYield}))})),o,u);if(!g.rows.length)continue;const S=g.rows.map($=>[h($.label),...$.byGroup.map(v=>v.passRatePercent===null?"\u2014":`${v.passRatePercent.toFixed(1)}%`),$.overall.passRatePercent===null?"\u2014":`${$.overall.passRatePercent.toFixed(1)}%`]),D=u==="spec"?"Pass rate \xB7 spec limits":u==="testFlag"?"Pass rate \xB7 tester flag":"Pass rate \xB7 functional";g.disagreementDies&&(y=g.disagreementDies),p.push(`<h4 class="report-subheading">${h(D)}</h4>`+B(["Test",...g.groups,"All"],S,{className:"compact"}))}y&&p.push(`<p class="report-note">${y.toLocaleString()} dies judged differently by limits and tester flag.</p>`),i.push(`<h3 class="report-subheading">${h(d.label)}</h3>${p.join(`
|
|
33
|
+
`)}`)}if(!i.length)return"";const l=r.length>e.length?`<p class="report-note">${r.length-e.length} further splittable field(s) not shown.</p>`:"";return x("Split Comparison",i.join(`
|
|
34
|
+
`)+l)}function te(t,o,s,n,r){const e=new Map,i=s==="hard"?"hardBinCounts":"softBinCounts";if(n?.length&&n.every(a=>a.stats[i]!==void 0))for(const a of n)for(const[c,b]of Object.entries(a.stats[i])){const p=Number(c);e.set(p,(e.get(p)??0)+b)}else for(const a of t){if(a.partial||a.edgeExcluded)continue;const c=s==="hard"?a.hbin:a.sbin;c!=null&&e.set(c,(e.get(c)??0)+1)}if(!e.size)return"";const l=[...e.values()].reduce((a,c)=>a+c,0),d=o?new Map(o.map(a=>[a.bin,a])):null,f=z(e.entries(),$e(r)[s]).map(([a,c])=>{const b=d?.get(a);return[b?.name?`Bin ${a} \xB7 ${b.name} (${c})`:`Bin ${a} (${c})`,C(c,l)]});return x(s==="hard"?"Hard Bin Breakdown (All Wafers)":"Soft Bin Breakdown (All Wafers)",B(["Bin","Yield"],f,{className:"compact"}))}function ne(t,o,s,n,r,e){const i=ve(s,n,r,l=>e[l],o).map(l=>[l.label,`${l.yieldPercent.toFixed(1)}%`]);return i.length?x(t,B(["Region","Yield"],i,{className:"compact"})):""}function qe(t,o,s){let n;if(s?.length&&s.every(r=>r.stats.perTestStats!==void 0)){const r=new Map;for(const e of s)for(const i of e.stats.perTestStats??[]){const l=r.get(i.testNumber);l?(l.n+=i.count,l.sum+=i.mean*i.count,l.min=Math.min(l.min,i.min),l.max=Math.max(l.max,i.max)):r.set(i.testNumber,{n:i.count,sum:i.mean*i.count,min:i.min,max:i.max})}n=[...r.entries()].map(([e,i])=>({testNumber:e,min:i.min,max:i.max,mean:i.sum/i.n}))}return q(t,o,n)}function Je(t,o,s){const n=He(s);return J(t,o,n)}const E=["lot","product","testProgram","temperature"];function Xe(t){const o=new Map(E.map(e=>[e,new Set]));for(const e of t)for(const i of E){const l=e.wafer?.metadata?.[i];l!=null&&l!==""&&o.get(i).add(String(l))}const s=E.filter(e=>o.get(e).size>1);if(s.length===0)return[{label:"",items:t,varying:s}];const n=new Map,r=[];for(const e of t){const i=s.map(l=>String(e.wafer?.metadata?.[l]??"(none)")).join(" \xB7 ");n.has(i)||(n.set(i,[]),r.push(i)),n.get(i).push(e)}return r.map(e=>({label:e,items:n.get(e),varying:s}))}function se(t,o,s,n,r,e,i){const l=Te(t.map(m=>m.source??m),{perWaferSummaries:t.map(m=>m.statsSummary),...i}),d=[],f=[],a=[],c=[];for(const m of t){const T=m.dies??[];m.wafer&&(d.push(m.wafer),a.push(T),c.push(R(m,r))),T.length&&f.push(...T)}const b=t.map(m=>({dies:m.dies??[],passBins:R(m,r)})),p=f.some(m=>m.hbin!=null),w=f.some(m=>m.sbin!=null),y=p||w,u=l.perWafer.map(m=>m.summary.stats.yieldPercent).filter(m=>m!==null),g=u.length?u.reduce((m,T)=>m+T,0)/u.length:null;let S=0,D=0,$=0,v=0,P=0;for(const m of b){const T=new Set(m.passBins);for(const W of m.dies){if(S++,W.edgeExcluded&&v++,W.partial&&P++,W.partial||W.edgeExcluded)continue;const H=U(W,T);H!==void 0&&(D++,H&&$++)}}const Y=D-$,k=D>0?$/D*100:null,ie=[{label:"Wafers",value:String(l.stats.waferCount)},{label:"Total dies",value:String(S)},...y?[{label:"Good dies",value:String($)},{label:"Bad dies",value:String(Y)}]:[],...v>0?[{label:"Edge excluded",value:String(v)}]:[],...P>0?[{label:"Partial dies",value:String(P)}]:[],...g!==null?[{label:"Mean wafer yield",value:`${g.toFixed(1)}%`}]:[],...k!==null?[{label:"Total yield",value:`${k.toFixed(1)}%`}]:[]],oe=G(l.perWafer.map(m=>m.summary.wafer)),re=x(oe.lotId!==void 0?"Lot Summary":"Summary",_(ie)),le=V(t.map(m=>({metadata:m.wafer?.metadata}))),ce=x("Per-Wafer Yield",Ve(l,t)),de=Qe(t,n,r),M=l.perWafer.map(m=>m.summary),ue=y?p?te(f,o,"hard",M,b):te(f,s,"soft",M,b):"",fe=y&&d.length?ne("Ring Yield (All Wafers)",j,a,d,e,c):"",me=y&&d.length?ne("Quadrant Yield (All Wafers)",O,a,d,e,c):"",pe=n.length?qe(f,n,M):"",ge=n.length?Je(f,n,M):"",he=n.length?X(t,n):"",be=l.findings.length?ee(l.findings,l.stats.waferCount):"",ye=_e(l),Se=[re,le,ce,de,ue,fe,me,pe,ge,he,be,ye].filter(Boolean).join(`
|
|
35
|
+
`);return{lotSummary:l,sections:Se}}function ae(t,o,s){return`<main class="report">
|
|
36
36
|
<header class="report-header">
|
|
37
|
-
<h1>${
|
|
38
|
-
<p class="report-subtitle">Generated ${
|
|
37
|
+
<h1>${h(t)}</h1>
|
|
38
|
+
<p class="report-subtitle">Generated ${h(s)}</p>
|
|
39
39
|
</header>
|
|
40
|
-
${
|
|
41
|
-
<p class="footer">Generated ${
|
|
42
|
-
</main>`}export function renderLotSummaryReportHtml(
|
|
40
|
+
${o}
|
|
41
|
+
<p class="footer">Generated ${h(s)}</p>
|
|
42
|
+
</main>`}export function renderLotSummaryReportHtml(t,o={}){const{items:s,hbinDefs:n,sbinDefs:r,testDefs:e=[],passBins:i=[1],ringCount:l=4,analyzeOptions:d}=t,f=new Date().toLocaleString(),a=Xe(s);if(a.length===1){const{lotSummary:y,sections:u}=se(a[0].items,n,r,e,i,l,d),g=G(y.perWafer.map(D=>D.summary.wafer)),S=o.title??(g.lotId!==void 0?`Lot Summary \u2014 ${g.lotId}`:`Summary \u2014 ${Ne(g)}`);return`<!DOCTYPE html>
|
|
43
43
|
<html lang="en">
|
|
44
44
|
<head>
|
|
45
45
|
<meta charset="UTF-8">
|
|
46
|
-
<title>${S
|
|
46
|
+
<title>${h(S)}</title>
|
|
47
47
|
<style>
|
|
48
|
-
${
|
|
48
|
+
${N()}
|
|
49
49
|
</style>
|
|
50
50
|
</head>
|
|
51
51
|
<body>
|
|
52
|
-
${
|
|
52
|
+
${ae(S,u,f)}
|
|
53
53
|
</body>
|
|
54
|
-
</html>`}const c=
|
|
55
|
-
${
|
|
54
|
+
</html>`}const c=o.title??"Summary",b=a.map(y=>{const{lotSummary:u,sections:g}=se(y.items,n,r,e,i,l,d),S=y.varying.includes("lot")?void 0:u.lot?.lot??u.lot?.lotId,D=S?`${y.label} \xB7 Lot ${String(S)}`:y.label;return ae(`${c} \u2014 ${D}`,g,f)}),p=`<p style="max-width:1080px;margin:0 auto 8px;padding:8px 12px;background:var(--report-surface);border:1px solid var(--report-line);border-radius:4px;font-size:12px;color:var(--report-muted);">This load spans ${a.length} groups by identity \u2014 shown separately below so stats are never pooled across them.</p>`,w=b.map((y,u)=>`${u===0?"":'<hr style="max-width:1080px;margin:32px auto 0;border:none;border-top:3px solid var(--report-line-strong);">'}
|
|
55
|
+
${y}`).join(`
|
|
56
56
|
`);return`<!DOCTYPE html>
|
|
57
57
|
<html lang="en">
|
|
58
58
|
<head>
|
|
59
59
|
<meta charset="UTF-8">
|
|
60
|
-
<title>${
|
|
60
|
+
<title>${h(c)}</title>
|
|
61
61
|
<style>
|
|
62
|
-
${
|
|
62
|
+
${N()}
|
|
63
63
|
</style>
|
|
64
64
|
</head>
|
|
65
65
|
<body>
|
|
66
66
|
<div class="report" style="padding-top:24px;">
|
|
67
|
-
${
|
|
67
|
+
${p}
|
|
68
68
|
</div>
|
|
69
69
|
${w}
|
|
70
70
|
</body>
|
|
71
|
-
</html>`}export{
|
|
71
|
+
</html>`}export{Be as openHtmlReport};export function renderWaferReportHtml(t,o,s={}){return renderSummaryReportHtml({wafer:t.wafer,dies:t.dies,yieldSummary:t.yield,dataCoverage:t.dataCoverage,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,testDefs:t.testDefs,statsSummary:o??t.statsSummary??("view"in t?Pe(t):void 0),passBins:[...t.passBins],ringCount:t.ringCount},s)}export function renderLotReportHtml(t,o={}){const s=I(t.map(e=>e.hbinDefs)),n=I(t.map(e=>e.sbinDefs)),r=t.some(e=>e.testDefs?.length)?Ye([...t]).defs:void 0;return renderLotSummaryReportHtml({items:t.map((e,i)=>({label:L(e,i),wafer:e.wafer,dies:e.dies,passBins:[...e.passBins],statsSummary:e.statsSummary,source:"view"in e?e:void 0})),hbinDefs:s.length?s:void 0,sbinDefs:n.length?n:void 0,testDefs:r,ringCount:t[0]?.ringCount,analyzeOptions:o.analyzeOptions},{title:o.title})}
|
|
@@ -5,7 +5,6 @@ export interface MetricItem {
|
|
|
5
5
|
value: string;
|
|
6
6
|
hint?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function escHtml(value: string): string;
|
|
9
8
|
export declare function renderSection(title: string, body: string, className?: string): string;
|
|
10
9
|
export declare function renderDefinitionList(entries: Array<{
|
|
11
10
|
label: string;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import{fmt as n}from"../renderer/fmt.js";import{buildFacetTable as
|
|
2
|
-
<h2>${
|
|
1
|
+
import{fmt as n}from"../renderer/fmt.js";import{buildFacetTable as d,prettyKey as c}from"./facets.js";import{escHtml as a}from"../core/utils.js";export function renderSection(e,t,r=""){return`<section class="${["report-section",r].filter(Boolean).join(" ")}">
|
|
2
|
+
<h2>${a(e)}</h2>
|
|
3
3
|
${t}
|
|
4
|
-
</section>`}export function renderDefinitionList(e,t="meta-list"){if(!e.length)return"";const r=e.map(o=>`<div class="definition-item"><dt>${
|
|
4
|
+
</section>`}export function renderDefinitionList(e,t="meta-list"){if(!e.length)return"";const r=e.map(o=>`<div class="definition-item"><dt>${a(o.label)}</dt><dd>${a(o.value)}</dd></div>`).join(`
|
|
5
5
|
`);return`<dl class="${t}">
|
|
6
6
|
${r}
|
|
7
|
-
</dl>`}export function buildMetadataRows(e){return
|
|
7
|
+
</dl>`}export function buildMetadataRows(e){return d(e,{facetableOnly:e.length>1}).map(r=>({label:c(r.key),value:r.values.map(o=>o.value).join(", ")}))}export function renderMetadataSection(e){const t=buildMetadataRows(e);return t.length?renderSection("Wafer Info",renderDefinitionList(t)):""}export function renderMetricGrid(e){return e.length?`<dl class="metric-grid">
|
|
8
8
|
${e.map(r=>`<div class="metric">
|
|
9
|
-
<dt>${
|
|
10
|
-
<dd>${
|
|
11
|
-
${r.hint?`<span class="metric-hint">${
|
|
9
|
+
<dt>${a(r.label)}</dt>
|
|
10
|
+
<dd>${a(r.value)}</dd>
|
|
11
|
+
${r.hint?`<span class="metric-hint">${a(r.hint)}</span>`:""}
|
|
12
12
|
</div>`).join(`
|
|
13
13
|
`)}
|
|
14
|
-
</dl>`:""}export function renderTable(e,t,r={}){if(!t.length)return`<p class="no-data">${
|
|
14
|
+
</dl>`:""}export function renderTable(e,t,r={}){if(!t.length)return`<p class="no-data">${a(r.emptyMessage??"No data")}</p>`;const o=["report-table",r.className].filter(Boolean).join(" "),p=e.map(i=>`<th>${a(i)}</th>`).join(""),l=t.map(i=>`<tr>${i.map(s=>`<td>${s}</td>`).join("")}</tr>`).join(`
|
|
15
15
|
`);return`<table class="${o}">
|
|
16
|
-
<thead><tr>${
|
|
17
|
-
<tbody>${
|
|
16
|
+
<thead><tr>${p}</tr></thead>
|
|
17
|
+
<tbody>${l}</tbody>
|
|
18
18
|
</table>`}export function renderSeverityBadge(e){const t=e.charAt(0).toUpperCase()+e.slice(1);return`<span class="badge badge-${e}">${t}</span>`}export function formatFindingDelta(e){const t=e.effect.absoluteDelta??0,r=t>0?"+":t<0?"\u2212":"";if(e.variable.kind==="yield"||e.variable.kind==="hardBin"||e.variable.kind==="softBin")return`${r}${Math.abs(t*100).toFixed(1)} pp`;if(e.variable.kind==="test"){if(e.effect.relativeDelta!==void 0&&Number.isFinite(e.effect.relativeDelta)){const o=Math.abs(e.effect.relativeDelta*100).toFixed(1);return`${r}${o}%`}return`${r}${n(Math.abs(t),e.variable.unit)}`}return`${r}${n(Math.abs(t),e.variable.unit)}`}export function formatFindingTooltip(e){return e.summary}export function formatFindingCoverage(e,t){return t!==void 0?`${e.stats.sampleSizeLeft}/${t}`:`${e.stats.sampleSizeLeft}/${e.stats.sampleSizeRight}`}export function reportStyles(){return`
|
|
19
19
|
:root {
|
|
20
20
|
--report-font: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Die, Wafer } from '../core/index.js';
|
|
2
|
+
import type { TestDef } from '../renderer/buildWaferMap.js';
|
|
3
|
+
import type { TestCapability, TestVerdictYield, RegionYieldFigures, StatsSummary } from './types.js';
|
|
4
|
+
/** Capability over `items` (each one wafer — the subgroup), restricted to `testNumbers`. */
|
|
5
|
+
export declare function computeCapability(items: {
|
|
6
|
+
dies: Die[];
|
|
7
|
+
}[], testDefs: TestDef[] | undefined, testNumbers: readonly number[]): TestCapability[] | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Lot capability pooled from each wafer's own `stats.capability` — exact, and
|
|
10
|
+
* without revisiting a die. Absent unless every wafer has capability, like the
|
|
11
|
+
* other pooled lot figures.
|
|
12
|
+
*/
|
|
13
|
+
export declare function poolCapability(summaries: StatsSummary[], testDefs: TestDef[]): TestCapability[] | undefined;
|
|
14
|
+
/** Pass rates by the tester's recorded verdict, plus the spec/verdict disagreement count. */
|
|
15
|
+
export declare function computeTestFlagYield(dies: Die[], testDefs: TestDef[] | undefined): {
|
|
16
|
+
testFlagYield?: TestVerdictYield[];
|
|
17
|
+
specVerdictDisagreementDies?: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Ring and quadrant yield over one or more wafers, each die judged by its own
|
|
21
|
+
* wafer's pass bins and rings by its own ring count. Rings are pooled only when
|
|
22
|
+
* every wafer has the same ring count — otherwise there are no common rings
|
|
23
|
+
* (the gallery raises `ring-count-mixed` for the same case) and `ring` is absent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function computeRegionYield(maps: {
|
|
26
|
+
dies: Die[];
|
|
27
|
+
wafer: Wafer;
|
|
28
|
+
passBins?: readonly number[];
|
|
29
|
+
ringCount: number;
|
|
30
|
+
}[]): RegionYieldFigures | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Lot region yield pooled from each wafer's own `regionYield`: pass and die
|
|
33
|
+
* counts summed per region key, first-appearance order. Pooled from the
|
|
34
|
+
* summaries rather than recomputed over every die, so a lot analysis given
|
|
35
|
+
* `perWaferSummaries` stays cheap. Same rule as the pass rates: absent unless
|
|
36
|
+
* every wafer has it, and rings pool only when every wafer has the same ring
|
|
37
|
+
* count (the gallery raises `ring-count-mixed` for the same case).
|
|
38
|
+
*/
|
|
39
|
+
export declare function poolRegionYield(summaries: StatsSummary[], ringCounts: readonly number[]): RegionYieldFigures | undefined;
|
|
40
|
+
/** The lot-level pass-rate figures: each wafer's counts, summed per test, only when every wafer has them. */
|
|
41
|
+
export declare function poolPassRates(summaries: StatsSummary[]): {
|
|
42
|
+
testSpecYield?: NonNullable<StatsSummary['stats']['testSpecYield']>;
|
|
43
|
+
functionalYield?: NonNullable<StatsSummary['stats']['functionalYield']>;
|
|
44
|
+
testFlagYield?: TestVerdictYield[];
|
|
45
|
+
specVerdictDisagreementDies?: number;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=summaryFigures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{itemPassBins as D}from"../core/passBins.js";import{buildCapabilityFigures as m,poolCapabilityFigures as h}from"./capability.js";import{buildTestPassRateData as y,hasJudgeableTests as w,poolFunctionalYield as b}from"./testPassRate.js";import{buildRegionYieldData as c,buildRingRegions as Y,buildQuadrantRegions as R}from"./regions.js";export function computeCapability(t,i,n){if(!i?.length||!n.length)return;const o=new Set(n),r=m(t,i.filter(d=>o.has(d.testNumber)));return r.length?r:void 0}export function poolCapability(t,i){const n=t.map(r=>r.stats.capability);if(!n.length||n.some(r=>r===void 0))return;const o=h(n,i);return o.length?o:void 0}export function computeTestFlagYield(t,i){if(!w(i,"testFlag",t))return{};const n=y([{key:"",items:[{dies:t}]}],i,"testFlag"),o={};return n.rows.length&&(o.testFlagYield=n.rows.map(r=>({testNumber:r.testNumber,label:r.label,...r.overall}))),n.disagreementDies!==null&&(o.specVerdictDisagreementDies=n.disagreementDies),o}export function computeRegionYield(t){if(!t.length)return;const i=t.map(l=>l.dies),n=t.map(l=>l.wafer),o=l=>D(t[l]),r=new Set(t.map(l=>l.ringCount)),d=c(i,n,t[0].ringCount,o,R),s=r.size===1?c(i,n,t[0].ringCount,o,Y):void 0;if(!(!d.length&&!s?.length))return{...s?.length?{ring:s}:{},quadrant:d}}export function poolRegionYield(t,i){const n=t.map(s=>s.stats.regionYield);if(!n.length||n.some(s=>s===void 0))return;const o=s=>{if(s.some(a=>a===void 0))return;const l=new Map;for(const a of s)for(const u of a){const f=l.get(u.key);f?(f.n+=u.n,f.passDies+=u.passDies):l.set(u.key,{...u})}const e=[...l.values()];for(const a of e)a.yieldPercent=a.passDies/a.n*100;return e.length?e:void 0},r=o(n.map(s=>s.quadrant)),d=new Set(i).size===1?o(n.map(s=>s.ring)):void 0;if(!(!r&&!d))return{...d?{ring:d}:{},quadrant:r??[]}}function p(t,i,n){if(!t.length||t.some(d=>d===void 0))return;const o=new Map;for(const d of t)for(const s of d??[]){const l=o.get(s.testNumber);l?i(l,s):o.set(s.testNumber,{...s})}if(!o.size)return;const r=[...o.values()];return r.forEach(n),r}const g=(t,i)=>i===0?null:t/i*100;export function poolPassRates(t){const i={},n=p(t.map(e=>e.stats.testSpecYield),(e,a)=>{e.passDies+=a.passDies,e.failLowDies+=a.failLowDies,e.failHighDies+=a.failHighDies,e.totalDies+=a.totalDies},e=>{e.yieldPercent=g(e.passDies,e.totalDies)});n&&(i.testSpecYield=n);const o=(e,a)=>{e.passDies+=a.passDies,e.failDies+=a.failDies,e.totalDies+=a.totalDies},r=e=>{e.passRatePercent=g(e.passDies,e.totalDies)},d=b(t);d&&(i.functionalYield=d);const s=p(t.map(e=>e.stats.testFlagYield),o,r);s&&(i.testFlagYield=s.sort((e,a)=>(e.passRatePercent??1/0)-(a.passRatePercent??1/0)));const l=t.map(e=>e.stats.specVerdictDisagreementDies);return l.length&&l.every(e=>e!==void 0)&&(i.specVerdictDisagreementDies=l.reduce((e,a)=>e+a,0)),i}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PatternClassification } from './patternClassification.js';
|
|
1
2
|
import type { WaferMapInput, WaferMapResult, WaferWarning } from '../renderer/buildWaferMap.js';
|
|
2
3
|
export type { WaferWarning };
|
|
3
4
|
export type StatsSeverity = 'info' | 'notable' | 'unusual';
|
|
@@ -266,8 +267,108 @@ export interface StatsSummary {
|
|
|
266
267
|
q1: number;
|
|
267
268
|
q3: number;
|
|
268
269
|
}>;
|
|
270
|
+
/**
|
|
271
|
+
* Cp/Cpk/Pp/Ppk for each parametric test with values, worst first: tests with
|
|
272
|
+
* both limits by Ppk, then tests without by variability. Populated under the
|
|
273
|
+
* same conditions as `perTestStats` and over the same tests.
|
|
274
|
+
*/
|
|
275
|
+
capability?: TestCapability[];
|
|
276
|
+
/**
|
|
277
|
+
* Per-test pass rate by the tester's recorded verdict, for parametric tests
|
|
278
|
+
* with recorded verdicts — the counterpart of `testSpecYield` (spec limits)
|
|
279
|
+
* and `functionalYield` (functional tests). Worst first.
|
|
280
|
+
*/
|
|
281
|
+
testFlagYield?: TestVerdictYield[];
|
|
282
|
+
/**
|
|
283
|
+
* Dies whose spec-limit judgement and recorded tester verdict disagree, across
|
|
284
|
+
* every parametric test that has both. Absent when no test has both sources;
|
|
285
|
+
* `0` means both are present and agree everywhere. Not an error — guard bands
|
|
286
|
+
* make disagreement expected — but worth a look.
|
|
287
|
+
*/
|
|
288
|
+
specVerdictDisagreementDies?: number;
|
|
289
|
+
/** Yield by ring and by quadrant (positioned dies only). */
|
|
290
|
+
regionYield?: RegionYieldFigures;
|
|
291
|
+
/**
|
|
292
|
+
* The spatial pattern classifier's result for this wafer — its label, confidence
|
|
293
|
+
* and the geometry features it measured (failure density overall and at the edge,
|
|
294
|
+
* the failing cluster's radial position, eccentricity, linearity, …). Present for
|
|
295
|
+
* every wafer the classifier could measure, including `'random'` and `'none'`,
|
|
296
|
+
* which raise no finding — so the features can train or feed a model of your own,
|
|
297
|
+
* negatives included. Absent when the map has no bin data, or too few failing dies
|
|
298
|
+
* to have a shape (fewer than 5, or 0.3% of the wafer).
|
|
299
|
+
*/
|
|
300
|
+
spatialPattern?: PatternClassification;
|
|
269
301
|
};
|
|
270
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Process capability for one parametric test.
|
|
305
|
+
*
|
|
306
|
+
* Cp/Cpk use `stdWithin` — the pooled within-wafer sample stddev, each wafer
|
|
307
|
+
* being the short-term subgroup — and Pp/Ppk use `stdOverall`. For a single
|
|
308
|
+
* wafer there is one subgroup, so `stdWithin` equals `stdOverall` and Cp equals
|
|
309
|
+
* Pp. Partial and edge-excluded dies are excluded, as for yield.
|
|
310
|
+
*/
|
|
311
|
+
export interface TestCapability {
|
|
312
|
+
testNumber: number;
|
|
313
|
+
label: string;
|
|
314
|
+
unit?: string;
|
|
315
|
+
/** Both `limitLow` and `limitHigh` defined. When false, `lsl`/`usl` are absent and every index is null. */
|
|
316
|
+
hasSpec: boolean;
|
|
317
|
+
lsl?: number;
|
|
318
|
+
usl?: number;
|
|
319
|
+
mean: number;
|
|
320
|
+
/** Sample stddev (ddof = 1) over every value. */
|
|
321
|
+
stdOverall: number;
|
|
322
|
+
/** Pooled within-wafer sample stddev. NaN when no wafer contributed two or more values. */
|
|
323
|
+
stdWithin: number;
|
|
324
|
+
/** Values counted. */
|
|
325
|
+
n: number;
|
|
326
|
+
/** Null when `hasSpec` is false or `stdWithin` is NaN or 0. */
|
|
327
|
+
cp: number | null;
|
|
328
|
+
cpk: number | null;
|
|
329
|
+
/** Null when `hasSpec` is false or `stdOverall` is 0. */
|
|
330
|
+
pp: number | null;
|
|
331
|
+
ppk: number | null;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Pass rate for one test, judged by the tester's own recorded verdict
|
|
335
|
+
* (`die.testPass`, STDF PTR `TEST_FLG`) — which exists whether or not spec
|
|
336
|
+
* limits do, and can legitimately disagree with a spec-limit judgement (guard
|
|
337
|
+
* bands, dynamic or per-site limits). Read through `getTestPassStatus`; dies
|
|
338
|
+
* with no recorded verdict are not counted as fails.
|
|
339
|
+
*/
|
|
340
|
+
export interface TestVerdictYield {
|
|
341
|
+
testNumber: number;
|
|
342
|
+
label: string;
|
|
343
|
+
passDies: number;
|
|
344
|
+
failDies: number;
|
|
345
|
+
/** Dies with a recorded verdict for this test. */
|
|
346
|
+
totalDies: number;
|
|
347
|
+
/** `(passDies / totalDies) × 100`, or `null` when no die had a verdict. */
|
|
348
|
+
passRatePercent: number | null;
|
|
349
|
+
}
|
|
350
|
+
/** Yield of one wafer region, each die judged by its own wafer's pass bins. */
|
|
351
|
+
export interface RegionYield {
|
|
352
|
+
/** Stable identity, e.g. `"ring:2"` or `"quadrant:NE"`. Use it as a key; do not parse it. */
|
|
353
|
+
key: string;
|
|
354
|
+
/** Display name, e.g. `"Ring 4 (edge)"` or `"NE"`. */
|
|
355
|
+
label: string;
|
|
356
|
+
/** `(pass / n) × 100`, in [0, 100]. */
|
|
357
|
+
yieldPercent: number;
|
|
358
|
+
/** Yield-eligible dies with a bin in this region. */
|
|
359
|
+
n: number;
|
|
360
|
+
/** Of those, the dies that pass — so regions can be pooled exactly. */
|
|
361
|
+
passDies: number;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Yield by ring and by quadrant — the Summary panel's region yield. Regions with
|
|
365
|
+
* no eligible dies are omitted. `ring` is absent from a lot summary whose wafers
|
|
366
|
+
* were built with different ring counts, which have no common rings to pool.
|
|
367
|
+
*/
|
|
368
|
+
export interface RegionYieldFigures {
|
|
369
|
+
ring?: RegionYield[];
|
|
370
|
+
quadrant: RegionYield[];
|
|
371
|
+
}
|
|
271
372
|
export interface LotStatsSummary {
|
|
272
373
|
/** Discriminant — always `'lot'`. Narrow on this to tell a lot summary from a {@link StatsSummary}. */
|
|
273
374
|
level: 'lot';
|
|
@@ -302,6 +403,23 @@ export interface LotStatsSummary {
|
|
|
302
403
|
/** Engine-computed analysis stats for this lot. */
|
|
303
404
|
stats: {
|
|
304
405
|
waferCount: number;
|
|
406
|
+
/**
|
|
407
|
+
* Cp/Cpk/Pp/Ppk over the whole lot, each wafer as a subgroup — so `stdWithin`
|
|
408
|
+
* is the pooled within-wafer stddev and Cp and Pp genuinely differ. Present
|
|
409
|
+
* when per-test statistics were computed (`computePerTestStats` or
|
|
410
|
+
* `enableTestValueAnalysis`). Worst first.
|
|
411
|
+
*/
|
|
412
|
+
capability?: TestCapability[];
|
|
413
|
+
/** `testSpecYield` pooled over the lot: counts summed per test. Absent unless every wafer has it. */
|
|
414
|
+
testSpecYield?: NonNullable<StatsSummary['stats']['testSpecYield']>;
|
|
415
|
+
/** `functionalYield` pooled over the lot: counts summed per test. Absent unless every wafer has it. */
|
|
416
|
+
functionalYield?: NonNullable<StatsSummary['stats']['functionalYield']>;
|
|
417
|
+
/** `testFlagYield` pooled over the lot: counts summed per test. Absent unless every wafer has it. */
|
|
418
|
+
testFlagYield?: TestVerdictYield[];
|
|
419
|
+
/** `specVerdictDisagreementDies` summed over the lot. Absent unless every wafer has it. */
|
|
420
|
+
specVerdictDisagreementDies?: number;
|
|
421
|
+
/** Ring and quadrant yield pooled over the lot: counts summed per region, each wafer judged by its own pass bins. Absent unless every wafer has it; `ring` also absent when ring counts differ. */
|
|
422
|
+
regionYield?: RegionYieldFigures;
|
|
305
423
|
};
|
|
306
424
|
/** Per-wafer yield as a flat series, ordered by waferIndex. `yieldPercent` is in [0, 100]; null when a wafer had no bin data. */
|
|
307
425
|
lotYieldSeries: Array<{
|
|
@@ -342,35 +460,6 @@ export interface LotStatsSummary {
|
|
|
342
460
|
}>;
|
|
343
461
|
}
|
|
344
462
|
export interface AnalyzeWaferMapOptions {
|
|
345
|
-
/**
|
|
346
|
-
* Should mirror the ring count used by the renderer for consistent semantics.
|
|
347
|
-
* Defaults to 4, which matches the current render default.
|
|
348
|
-
*/
|
|
349
|
-
ringCount?: number;
|
|
350
|
-
passBins?: number[];
|
|
351
|
-
/**
|
|
352
|
-
* REMOVED in 0.27.0 — `significanceLevel`, `minimumEffectSize` and
|
|
353
|
-
* `minimumRelativeEffect` are now internal constants, not options.
|
|
354
|
-
*
|
|
355
|
-
* They decide what counts as a finding, so a wrong value does not make the
|
|
356
|
-
* output *look* different, it makes it wrong — and silently. A negative
|
|
357
|
-
* `significanceLevel` returned zero findings across the board, which reads as
|
|
358
|
-
* "nothing wrong with this wafer": the worst failure an analysis tool has.
|
|
359
|
-
* `minimumSampleSize` was already internal for exactly this reason, and
|
|
360
|
-
* `adaptOptions()` declines to adapt `significanceLevel` even internally
|
|
361
|
-
* because it perturbs the multiple-comparison correction unpredictably. The
|
|
362
|
-
* library was being more careful with itself than with its callers.
|
|
363
|
-
*
|
|
364
|
-
* The gates are documented in docs/api.md §7.3.2, so a reader can still learn
|
|
365
|
-
* *why* a pattern did or did not produce a finding — which is what callers
|
|
366
|
-
* actually wanted. Values passed by untyped (plain-JS) callers are now
|
|
367
|
-
* validated and clamped rather than honoured; see `resolveOptions`.
|
|
368
|
-
*/
|
|
369
|
-
includePartial?: boolean;
|
|
370
|
-
includeEdgeExcluded?: boolean;
|
|
371
|
-
enableYieldAnalysis?: boolean;
|
|
372
|
-
enableHardBinAnalysis?: boolean;
|
|
373
|
-
enableSoftBinAnalysis?: boolean;
|
|
374
463
|
/**
|
|
375
464
|
* Full parametric **spatial significance** analysis: Welch comparisons of each
|
|
376
465
|
* test's values between every region (ring/quadrant/reticle/site/sector) and
|
|
@@ -393,22 +482,8 @@ export interface AnalyzeWaferMapOptions {
|
|
|
393
482
|
* Off by default. Implied by `enableTestValueAnalysis`.
|
|
394
483
|
*/
|
|
395
484
|
computePerTestStats?: boolean;
|
|
396
|
-
enableReticlePositionAnalysis?: boolean;
|
|
397
|
-
/**
|
|
398
|
-
* Analyse yield and bin distributions by test site (`siteNum`).
|
|
399
|
-
* Enabled automatically when the wafer contains meaningful site duplication
|
|
400
|
-
* (at least 2 distinct site numbers each appearing on 3 or more dies).
|
|
401
|
-
* Set to `false` to suppress, or `true` to force-enable regardless of the guard.
|
|
402
|
-
*/
|
|
403
|
-
enableTestSiteAnalysis?: boolean;
|
|
404
|
-
/** Detect contiguous failure clusters and edge arc damage. Default true. */
|
|
405
|
-
enableClusterAnalysis?: boolean;
|
|
406
|
-
/** Angular sector analysis (finer-grained than quadrants). Default true. */
|
|
407
|
-
enableAngularAnalysis?: boolean;
|
|
408
485
|
/** Number of angular sectors for sector analysis. Must be 4, 8, 16, or 32. Default 8. */
|
|
409
486
|
sectorCount?: number;
|
|
410
|
-
/** Classify the spatial failure pattern (center, edge-ring, scratch, etc.). Default true. */
|
|
411
|
-
enablePatternClassification?: boolean;
|
|
412
487
|
/**
|
|
413
488
|
* Restrict test value analysis to a specific subset of test numbers.
|
|
414
489
|
* When omitted and more than 250 tests are present in the data, test value
|
|
@@ -13,10 +13,10 @@ export interface ChartDatum {
|
|
|
13
13
|
* `label` — a caller with an `onOpen` callback should resolve back to its
|
|
14
14
|
* own item by `key`, not by re-searching for `label`. Labels are only
|
|
15
15
|
* guaranteed unique when the caller supplies one per item; an item with no
|
|
16
|
-
* `label`
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* `label` is named by its wafer ID (`waferDisplayLabel`, core/waferLabel.ts),
|
|
17
|
+
* and two wafers from different lots can share an ID — so two rows can carry
|
|
18
|
+
* an identical, ambiguous label, and matching on it would silently resolve
|
|
19
|
+
* to the wrong item. Absent on group rows
|
|
20
20
|
* (`itemCount > 1`), which are drilled into rather than opened directly.
|
|
21
21
|
*/
|
|
22
22
|
key?: string | number;
|
|
@@ -24,6 +24,12 @@ export interface ChartDatum {
|
|
|
24
24
|
export interface YieldItem {
|
|
25
25
|
label?: string;
|
|
26
26
|
dies?: Die[];
|
|
27
|
+
/**
|
|
28
|
+
* This item's own pass bins (`WaferMapResult.passBins`). Wins over the
|
|
29
|
+
* function's `passBins` argument, which only fills in for items without —
|
|
30
|
+
* a lot can mix wafers built with different pass bins.
|
|
31
|
+
*/
|
|
32
|
+
passBins?: readonly number[];
|
|
27
33
|
/**
|
|
28
34
|
* Precomputed yield percent for this item (e.g. from `analyzeWaferLot`'s
|
|
29
35
|
* `lotYieldSeries[waferIndex].yieldPercent`) — used directly when present.
|
|
@@ -35,7 +41,7 @@ export interface YieldItem {
|
|
|
35
41
|
}
|
|
36
42
|
export type YieldSortBy = 'yield' | 'label';
|
|
37
43
|
/** One bar per item. */
|
|
38
|
-
export declare function buildYieldData(items: YieldItem[], passBins?: number[], sortBy?: YieldSortBy): ChartDatum[];
|
|
44
|
+
export declare function buildYieldData(items: YieldItem[], passBins?: readonly number[], sortBy?: YieldSortBy): ChartDatum[];
|
|
39
45
|
/**
|
|
40
46
|
* Combined yield: one bar per group, pooling the group's items. The pooled
|
|
41
47
|
* yield is the yield-eligible-die-count-weighted mean of per-item yields —
|
|
@@ -52,5 +58,5 @@ export declare function buildYieldData(items: YieldItem[], passBins?: number[],
|
|
|
52
58
|
export declare function buildYieldDataCombined(groups: {
|
|
53
59
|
key: string;
|
|
54
60
|
items: YieldItem[];
|
|
55
|
-
}[], passBins?: number[], sortBy?: YieldSortBy): ChartDatum[];
|
|
61
|
+
}[], passBins?: readonly number[], sortBy?: YieldSortBy): ChartDatum[];
|
|
56
62
|
//# sourceMappingURL=yield.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isYieldEligibleDie as c,diePassStatus as p}from"../core/dies.js";import{compareNatural as b}from"../core/utils.js";function m(e,n){let i=0,t=0;const l=new Set(n);for(const r of e){if(!c(r))continue;const o=p(r,l);o!==void 0&&(t++,o&&i++)}return t>0?i/t*100:0}function d(e,n){return e.yieldPercent!==void 0?e.yieldPercent??0:m(e.dies??[],n)}function y(e){let n=0;for(const i of e)c(i)&&n++;return n}function f(e,n){n==="yield"?e.sort((i,t)=>t.value-i.value):e.sort((i,t)=>b(i.label,t.label))}export function buildYieldData(e,n=[1],i="label"){const t=e.map((l,r)=>{const o=d(l,n);return{label:l.label??`#${r}`,value:o,percent:o,itemCount:1,key:l.key}});return f(t,i),t}export function buildYieldDataCombined(e,n=[1],i="label"){const t=e.map(l=>{let r=0,o=0;for(const a of l.items){const
|
|
1
|
+
import{isYieldEligibleDie as c,diePassStatus as p}from"../core/dies.js";import{compareNatural as b}from"../core/utils.js";function m(e,n){let i=0,t=0;const l=new Set(n);for(const r of e){if(!c(r))continue;const o=p(r,l);o!==void 0&&(t++,o&&i++)}return t>0?i/t*100:0}function d(e,n){return e.yieldPercent!==void 0?e.yieldPercent??0:m(e.dies??[],e.passBins??n)}function y(e){let n=0;for(const i of e)c(i)&&n++;return n}function f(e,n){n==="yield"?e.sort((i,t)=>t.value-i.value):e.sort((i,t)=>b(i.label,t.label))}export function buildYieldData(e,n=[1],i="label"){const t=e.map((l,r)=>{const o=d(l,n);return{label:l.label??`#${r}`,value:o,percent:o,itemCount:1,key:l.key}});return f(t,i),t}export function buildYieldDataCombined(e,n=[1],i="label"){const t=e.map(l=>{let r=0,o=0;for(const a of l.items){const u=y(a.dies??[]);r+=d(a,n)*u,o+=u}const s=o>0?r/o:0;return{label:l.key,value:s,percent:s,itemCount:l.items.length}});return f(t,i),t}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wafertools/wafermap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Interactive wafer map visualization and yield analysis for semiconductor test data. Hard bins, soft bins, test values, retests, edge exclusion, reticle overlays, spatial statistics, failure clustering, and lot-level trend analysis — pure ES modules, no runtime dependencies, no server required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|