Specific Process Knowledge/Lithography/EBeamLithography/thopesplaystuff: Difference between revisions
Blanked the page Tag: Blanking |
No edit summary |
||
| Line 1: | Line 1: | ||
<html> | |||
<!doctype html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<meta name="viewport" content="width=device-width,initial-scale=1" /> | |||
<title>EBL Dose Simulator — Hello Kitty Theme</title> | |||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> | |||
<style> | |||
:root{ | |||
--pink:#ffb6d5; | |||
--hotpink:#ff69b4; | |||
--accent:#fff0f6; | |||
--text:#333; | |||
--card:#fff; | |||
} | |||
body{ | |||
margin:0; | |||
font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial; | |||
background: linear-gradient(180deg,var(--accent),#fff); | |||
color:var(--text); | |||
} | |||
header{ | |||
display:flex;align-items:center;gap:16px;padding:18px 24px;background:var(--pink);box-shadow:0 6px 18px rgba(255,105,180,0.12); | |||
} | |||
.logo{ | |||
width:64px;height:64px;border-radius:12px;background:linear-gradient(180deg,var(--hotpink),#ffd1ea);display:flex;align-items:center;justify-content:center;box-shadow:0 6px 10px rgba(0,0,0,0.06); | |||
} | |||
.logo svg{width:46px;height:46px} | |||
h1{font-size:20px;margin:0} | |||
.container{max-width:1100px;margin:20px auto;padding:18px} | |||
.grid{display:grid;grid-template-columns:320px 1fr;gap:18px} | |||
.card{background:var(--card);border-radius:12px;padding:14px;box-shadow:0 8px 22px rgba(0,0,0,0.06)} | |||
label{font-size:13px;display:block;margin-bottom:6px} | |||
.small{font-size:12px;color:#666} | |||
input[type=range]{width:100%} | |||
.row{display:flex;gap:12px;align-items:center} | |||
button{background:var(--hotpink);border:none;color:white;padding:8px 12px;border-radius:10px;cursor:pointer} | |||
footer{margin-top:18px;text-align:center;color:#777;font-size:13px} | |||
.muted{color:#666;font-size:13px} | |||
canvas{background:transparent} | |||
</style> | |||
</head> | |||
<body> | |||
<header> | |||
<div class="logo" aria-hidden> | |||
<!-- Cute Hello-Kitty-like SVG (original, stylized cat) --> | |||
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> | |||
<g fill="none" stroke="#ffffff" stroke-width="1.6"> | |||
<ellipse cx="32" cy="36" rx="18" ry="14" fill="#fff" stroke="none"/> | |||
<path d="M18 18c-6 0-8-6-8-6s6 0 8 2c2 2 6 4 10 4s8-2 10-4c2-2 8-2 8-2s-2 6-8 6" fill="#fff" stroke="none"/> | |||
</g> | |||
<circle cx="24" cy="34" r="2.5" fill="#ff69b4"/> | |||
<circle cx="40" cy="34" r="2.5" fill="#ff69b4"/> | |||
<rect x="28" y="38" width="8" height="6" rx="3" fill="#ff69b4"/> | |||
</svg> | |||
</div> | |||
<div> | |||
<h1>EBL Dose Simulator — Hello Kitty Theme</h1> | |||
<div class="small">Simulate exposure dose including forward- and backscattered electrons (proximity effect)</div> | |||
</div> | |||
</header> | |||
<main class="container"> | |||
<div class="grid"> | |||
<div class="card"> | |||
<div style="margin-bottom:10px"> | |||
<label for="widthSlider">Line width: <strong id="widthLabel">100 nm</strong></label> | |||
<input id="widthSlider" type="range" min="10" max="400" value="100" step="1"> | |||
<div class="small muted">Change the width of the three parallel lines. Dose profile updates live.</div> | |||
</div> | |||
<div style="margin-top:12px"> | |||
<label for="doseInput">Incident dose (a.u.):</label> | |||
<input id="doseInput" type="number" value="1" step="0.1" style="width:100%" /> | |||
<div class="small muted">This is a scale factor — relative units (a.u.).</div> | |||
</div> | |||
<hr style="margin:12px 0;border:none;border-top:1px solid #f1d7e3"> | |||
<div> | |||
<label>Proximity function parameters</label> | |||
<div class="small">A two-Gaussian point-spread function is used: narrow forward-scatter (σ<sub>f</sub>) and broad backscatter (σ<sub>b</sub>) with weight η.</div> | |||
<div style="margin-top:8px"> | |||
<label>Forward sigma (σ<sub>f</sub>, nm): <strong id="sfLabel">5</strong></label> | |||
<input id="sfSlider" type="range" min="1" max="30" value="5"> | |||
</div> | |||
<div style="margin-top:8px"> | |||
<label>Backscatter sigma (σ<sub>b</sub>, nm): <strong id="sbLabel">200</strong></label> | |||
<input id="sbSlider" type="range" min="50" max="2000" value="200"> | |||
</div> | |||
<div style="margin-top:8px"> | |||
<label>Backscatter weight (η): <strong id="etaLabel">0.4</strong></label> | |||
<input id="etaSlider" type="range" min="0" max="0.9" step="0.01" value="0.4"> | |||
</div> | |||
</div> | |||
<hr style="margin:12px 0;border:none;border-top:1px solid #f1d7e3"> | |||
<div style="display:flex;gap:8px;justify-content:space-between"> | |||
<button id="resetBtn">Reset</button> | |||
<div class="small muted">Tip: drag the line-width slider to see how proximity effects blur and add dose between lines.</div> | |||
</div> | |||
</div> | |||
<div class="card"> | |||
<canvas id="doseChart" height="220"></canvas> | |||
<div style="display:flex;gap:14px;margin-top:10px;align-items:center"> | |||
<label style="display:flex;gap:6px;align-items:center"><input id="showPattern" type="checkbox" checked> Show pattern</label> | |||
<label style="display:flex;gap:6px;align-items:center"><input id="showForward" type="checkbox" checked> Forward component</label> | |||
<label style="display:flex;gap:6px;align-items:center"><input id="showBack" type="checkbox" checked> Backscatter component</label> | |||
</div> | |||
</div> | |||
</div> | |||
<footer> | |||
<div class="muted">Model: 1D cross-section of three parallel lines. Uses a two-Gaussian proximity function: PSF(r) = (1-η)*G(σf) + η*G(σb). Units are arbitrary — this is a relative-dose visualizer, useful for intuition and teaching.</div> | |||
</footer> | |||
</main> | |||
<script> | |||
// --- Configuration & utilities --- | |||
const LENGTH = 2000; // nm length of simulation window | |||
const DX = 1; // nm resolution | |||
const N = Math.floor(LENGTH/DX); | |||
const x = new Array(N).fill(0).map((_,i)=>i*DX); | |||
function gaussian1d(sigma){ | |||
const twiceVar = 2*sigma*sigma; | |||
const radius = Math.ceil(6*sigma/DX); | |||
const kernel = new Float64Array(radius*2+1); | |||
let sum = 0; | |||
for(let i=-radius;i<=radius;i++){ | |||
const v = Math.exp(-(i*DX)*(i*DX)/twiceVar); | |||
kernel[i+radius]=v; sum+=v; | |||
} | |||
for(let i=0;i<kernel.length;i++) kernel[i]/=sum; | |||
return {kernel, radius}; | |||
} | |||
function convolve(signal, kernel, radius){ | |||
const out = new Float64Array(signal.length); | |||
for(let i=0;i<signal.length;i++){ | |||
let s=0; | |||
for(let k=-radius;k<=radius;k++){ | |||
const j=i+k; | |||
if(j<0||j>=signal.length) continue; | |||
s += signal[j]*kernel[k+radius]; | |||
} | |||
out[i]=s; | |||
} | |||
return out; | |||
} | |||
function makePattern(lineWidth){ | |||
// three parallel lines centered at 600, 1000, 1400 nm | |||
const centers = [600,1000,1400]; | |||
const pat = new Float64Array(N); | |||
const half = lineWidth/2; | |||
for(let i=0;i<N;i++){ | |||
const xi = x[i]; | |||
for(const c of centers){ | |||
if(Math.abs(xi-c) <= half) { pat[i]=1; break; } | |||
} | |||
} | |||
return pat; | |||
} | |||
// --- UI elements --- | |||
const widthSlider = document.getElementById('widthSlider'); | |||
const widthLabel = document.getElementById('widthLabel'); | |||
const doseInput = document.getElementById('doseInput'); | |||
const sfSlider = document.getElementById('sfSlider'); | |||
const sbSlider = document.getElementById('sbSlider'); | |||
const etaSlider = document.getElementById('etaSlider'); | |||
const sfLabel = document.getElementById('sfLabel'); | |||
const sbLabel = document.getElementById('sbLabel'); | |||
const etaLabel = document.getElementById('etaLabel'); | |||
const resetBtn = document.getElementById('resetBtn'); | |||
const showPattern = document.getElementById('showPattern'); | |||
const showForward = document.getElementById('showForward'); | |||
const showBack = document.getElementById('showBack'); | |||
// --- Chart --- | |||
const ctx = document.getElementById('doseChart').getContext('2d'); | |||
const chart = new Chart(ctx, { | |||
type: 'line', | |||
data: { | |||
labels: x, | |||
datasets: [ | |||
{label:'Total dose', data: new Array(N).fill(0), tension:0.2, borderWidth:2, pointRadius:0, spanGaps:true}, | |||
{label:'Pattern (direct)', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, borderDash:[4,4], hidden:!showPattern.checked}, | |||
{label:'Forward scatter', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, hidden:!showForward.checked}, | |||
{label:'Backscatter', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, hidden:!showBack.checked}, | |||
] | |||
}, | |||
options: { | |||
animation:false, | |||
plugins:{legend:{position:'top'}}, | |||
scales: { | |||
x:{display:true,title:{display:true,text:'x (nm)'}}, | |||
y:{display:true,title:{display:true,text:'Relative dose (a.u.)'}} | |||
} | |||
} | |||
}); | |||
function updateChart(total, pattern, forwardComp, backComp){ | |||
chart.data.datasets[0].data = Array.from(total); | |||
chart.data.datasets[1].data = Array.from(pattern); | |||
chart.data.datasets[2].data = Array.from(forwardComp); | |||
chart.data.datasets[3].data = Array.from(backComp); | |||
chart.data.datasets[1].hidden = !showPattern.checked; | |||
chart.data.datasets[2].hidden = !showForward.checked; | |||
chart.data.datasets[3].hidden = !showBack.checked; | |||
chart.update('none'); | |||
} | |||
function computeAndRender(){ | |||
const w = Number(widthSlider.value); | |||
widthLabel.textContent = `${w} nm`; | |||
const doseScale = Number(doseInput.value) || 1; | |||
const sf = Number(sfSlider.value); sfLabel.textContent = sf; | |||
const sb = Number(sbSlider.value); sbLabel.textContent = sb; | |||
const eta = Number(etaSlider.value); etaLabel.textContent = eta; | |||
const pattern = makePattern(w); | |||
// forward kernel | |||
const {kernel: kf, radius: rf} = gaussian1d(sf); | |||
const {kernel: kb, radius: rb} = gaussian1d(sb); | |||
const forwardComp = convolve(pattern, kf, rf).map(v=>v*(1-eta)*doseScale); | |||
const backComp = convolve(pattern, kb, rb).map(v=>v*eta*doseScale); | |||
const total = new Float64Array(N); | |||
for(let i=0;i<N;i++) total[i]=forwardComp[i]+backComp[i]; | |||
updateChart(total, pattern.map(v=>v*doseScale), forwardComp, backComp); | |||
} | |||
// --- events --- | |||
[widthSlider, doseInput, sfSlider, sbSlider, etaSlider].forEach(el=>el.addEventListener('input',computeAndRender)); | |||
[showPattern, showForward, showBack].forEach(el=>el.addEventListener('change',computeAndRender)); | |||
resetBtn.addEventListener('click',()=>{ | |||
widthSlider.value=100; doseInput.value=1; sfSlider.value=5; sbSlider.value=200; etaSlider.value=0.4; | |||
computeAndRender(); | |||
}); | |||
// initial render | |||
computeAndRender(); | |||
</script> | |||
</body> | |||
</html> | |||
Revision as of 17:34, 2 September 2025
<html> <!doctype html> <html lang="en"> <head>
<meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>EBL Dose Simulator — Hello Kitty Theme</title> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> <style> :root{ --pink:#ffb6d5; --hotpink:#ff69b4; --accent:#fff0f6; --text:#333; --card:#fff; } body{ margin:0; font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial; background: linear-gradient(180deg,var(--accent),#fff); color:var(--text); } header{ display:flex;align-items:center;gap:16px;padding:18px 24px;background:var(--pink);box-shadow:0 6px 18px rgba(255,105,180,0.12); } .logo{ width:64px;height:64px;border-radius:12px;background:linear-gradient(180deg,var(--hotpink),#ffd1ea);display:flex;align-items:center;justify-content:center;box-shadow:0 6px 10px rgba(0,0,0,0.06); } .logo svg{width:46px;height:46px} h1{font-size:20px;margin:0} .container{max-width:1100px;margin:20px auto;padding:18px} .grid{display:grid;grid-template-columns:320px 1fr;gap:18px} .card{background:var(--card);border-radius:12px;padding:14px;box-shadow:0 8px 22px rgba(0,0,0,0.06)} label{font-size:13px;display:block;margin-bottom:6px} .small{font-size:12px;color:#666} input[type=range]{width:100%} .row{display:flex;gap:12px;align-items:center} button{background:var(--hotpink);border:none;color:white;padding:8px 12px;border-radius:10px;cursor:pointer} footer{margin-top:18px;text-align:center;color:#777;font-size:13px} .muted{color:#666;font-size:13px} canvas{background:transparent} </style>
</head> <body>
<header>
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> <g fill="none" stroke="#ffffff" stroke-width="1.6"> <ellipse cx="32" cy="36" rx="18" ry="14" fill="#fff" stroke="none"/> <path d="M18 18c-6 0-8-6-8-6s6 0 8 2c2 2 6 4 10 4s8-2 10-4c2-2 8-2 8-2s-2 6-8 6" fill="#fff" stroke="none"/> </g> <circle cx="24" cy="34" r="2.5" fill="#ff69b4"/> <circle cx="40" cy="34" r="2.5" fill="#ff69b4"/> <rect x="28" y="38" width="8" height="6" rx="3" fill="#ff69b4"/> </svg>
EBL Dose Simulator — Hello Kitty Theme
</header>
<main class="container">
<label for="widthSlider">Line width: 100 nm</label>
<input id="widthSlider" type="range" min="10" max="400" value="100" step="1">
<label for="doseInput">Incident dose (a.u.):</label>
<input id="doseInput" type="number" value="1" step="0.1" style="width:100%" />
<label>Proximity function parameters</label>
<label>Forward sigma (σf, nm): 5</label>
<input id="sfSlider" type="range" min="1" max="30" value="5">
<label>Backscatter sigma (σb, nm): 200</label>
<input id="sbSlider" type="range" min="50" max="2000" value="200">
<label>Backscatter weight (η): 0.4</label>
<input id="etaSlider" type="range" min="0" max="0.9" step="0.01" value="0.4">
<button id="resetBtn">Reset</button>
<canvas id="doseChart" height="220"></canvas>
<label style="display:flex;gap:6px;align-items:center"><input id="showPattern" type="checkbox" checked> Show pattern</label>
<label style="display:flex;gap:6px;align-items:center"><input id="showForward" type="checkbox" checked> Forward component</label>
<label style="display:flex;gap:6px;align-items:center"><input id="showBack" type="checkbox" checked> Backscatter component</label>
<footer>
</footer> </main>
<script> // --- Configuration & utilities --- const LENGTH = 2000; // nm length of simulation window const DX = 1; // nm resolution const N = Math.floor(LENGTH/DX); const x = new Array(N).fill(0).map((_,i)=>i*DX);
function gaussian1d(sigma){
const twiceVar = 2*sigma*sigma;
const radius = Math.ceil(6*sigma/DX);
const kernel = new Float64Array(radius*2+1);
let sum = 0;
for(let i=-radius;i<=radius;i++){
const v = Math.exp(-(i*DX)*(i*DX)/twiceVar);
kernel[i+radius]=v; sum+=v;
}
for(let i=0;i<kernel.length;i++) kernel[i]/=sum;
return {kernel, radius};
}
function convolve(signal, kernel, radius){
const out = new Float64Array(signal.length);
for(let i=0;i<signal.length;i++){
let s=0;
for(let k=-radius;k<=radius;k++){
const j=i+k;
if(j<0||j>=signal.length) continue;
s += signal[j]*kernel[k+radius];
}
out[i]=s;
}
return out;
}
function makePattern(lineWidth){
// three parallel lines centered at 600, 1000, 1400 nm
const centers = [600,1000,1400];
const pat = new Float64Array(N);
const half = lineWidth/2;
for(let i=0;i<N;i++){
const xi = x[i];
for(const c of centers){
if(Math.abs(xi-c) <= half) { pat[i]=1; break; }
}
}
return pat;
}
// --- UI elements --- const widthSlider = document.getElementById('widthSlider'); const widthLabel = document.getElementById('widthLabel'); const doseInput = document.getElementById('doseInput'); const sfSlider = document.getElementById('sfSlider'); const sbSlider = document.getElementById('sbSlider'); const etaSlider = document.getElementById('etaSlider'); const sfLabel = document.getElementById('sfLabel'); const sbLabel = document.getElementById('sbLabel'); const etaLabel = document.getElementById('etaLabel'); const resetBtn = document.getElementById('resetBtn'); const showPattern = document.getElementById('showPattern'); const showForward = document.getElementById('showForward'); const showBack = document.getElementById('showBack');
// --- Chart --- const ctx = document.getElementById('doseChart').getContext('2d'); const chart = new Chart(ctx, {
type: 'line',
data: {
labels: x,
datasets: [
{label:'Total dose', data: new Array(N).fill(0), tension:0.2, borderWidth:2, pointRadius:0, spanGaps:true},
{label:'Pattern (direct)', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, borderDash:[4,4], hidden:!showPattern.checked},
{label:'Forward scatter', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, hidden:!showForward.checked},
{label:'Backscatter', data: new Array(N).fill(0), tension:0.2, borderWidth:1, pointRadius:0, hidden:!showBack.checked},
]
},
options: {
animation:false,
plugins:{legend:{position:'top'}},
scales: {
x:{display:true,title:{display:true,text:'x (nm)'}},
y:{display:true,title:{display:true,text:'Relative dose (a.u.)'}}
}
}
});
function updateChart(total, pattern, forwardComp, backComp){
chart.data.datasets[0].data = Array.from(total);
chart.data.datasets[1].data = Array.from(pattern);
chart.data.datasets[2].data = Array.from(forwardComp);
chart.data.datasets[3].data = Array.from(backComp);
chart.data.datasets[1].hidden = !showPattern.checked;
chart.data.datasets[2].hidden = !showForward.checked;
chart.data.datasets[3].hidden = !showBack.checked;
chart.update('none');
}
function computeAndRender(){
const w = Number(widthSlider.value);
widthLabel.textContent = `${w} nm`;
const doseScale = Number(doseInput.value) || 1;
const sf = Number(sfSlider.value); sfLabel.textContent = sf;
const sb = Number(sbSlider.value); sbLabel.textContent = sb;
const eta = Number(etaSlider.value); etaLabel.textContent = eta;
const pattern = makePattern(w);
// forward kernel
const {kernel: kf, radius: rf} = gaussian1d(sf);
const {kernel: kb, radius: rb} = gaussian1d(sb);
const forwardComp = convolve(pattern, kf, rf).map(v=>v*(1-eta)*doseScale); const backComp = convolve(pattern, kb, rb).map(v=>v*eta*doseScale);
const total = new Float64Array(N); for(let i=0;i<N;i++) total[i]=forwardComp[i]+backComp[i];
updateChart(total, pattern.map(v=>v*doseScale), forwardComp, backComp);
}
// --- events --- [widthSlider, doseInput, sfSlider, sbSlider, etaSlider].forEach(el=>el.addEventListener('input',computeAndRender)); [showPattern, showForward, showBack].forEach(el=>el.addEventListener('change',computeAndRender)); resetBtn.addEventListener('click',()=>{
widthSlider.value=100; doseInput.value=1; sfSlider.value=5; sbSlider.value=200; etaSlider.value=0.4; computeAndRender();
});
// initial render computeAndRender(); </script> </body> </html>