195 lines
8.3 KiB
Python
Executable File
195 lines
8.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
preview_doping_3d.py - Parameterized 3D geometry and doping setup for BJT.
|
|
Generates a 3D mesh using Gmsh and exports a Tecplot file for ParaView preview.
|
|
"""
|
|
import sys
|
|
import os
|
|
import gmsh
|
|
import numpy as np
|
|
|
|
# Add virtual env path to ensure devsim is found
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from devsim import (
|
|
create_gmsh_mesh, add_gmsh_region, add_gmsh_contact,
|
|
finalize_mesh, create_device, get_device_list,
|
|
node_model, write_devices
|
|
)
|
|
|
|
# =============================================================================
|
|
# 1. 幾何參數定義 (單位: cm, 1 um = 1e-4 cm)
|
|
# =============================================================================
|
|
um = 1e-4
|
|
|
|
# 元件總尺寸
|
|
W_device = 50.0 * um # X 寬度
|
|
H_device = 25.0 * um # Y 深度 (朝基底方向)
|
|
L_device = 10.0 * um # Z 長度
|
|
|
|
# 電極尺寸與位置
|
|
# Emitter (X: 20~30 um, Z: 2~8 um, Y: 0 表面)
|
|
x_emit_center = 25.0 * um
|
|
w_emit = 10.0 * um
|
|
z_emit_center = 5.0 * um
|
|
l_emit = 6.0 * um
|
|
|
|
# Base (X: 40~45 um, Z: 2~8 um, Y: 0 表面)
|
|
x_base_center = 42.5 * um
|
|
w_base = 5.0 * um
|
|
z_base_center = 5.0 * um
|
|
l_base = 6.0 * um
|
|
|
|
# 網格控制尺寸 (加密以獲得平滑的 3D 界面)
|
|
mesh_size_min = 0.15 * um
|
|
mesh_size_max = 0.8 * um
|
|
|
|
# =============================================================================
|
|
# 2. 使用 Gmsh Python API 進行 3D 幾何與網格建模
|
|
# =============================================================================
|
|
print(">>> Step 1: Generating 3D geometry and mesh using Gmsh...")
|
|
gmsh.initialize()
|
|
gmsh.model.add("bjt_3d_device")
|
|
|
|
# 設定網格大小限制與輸出格式為 MSH v2.2
|
|
gmsh.option.setNumber("Mesh.CharacteristicLengthMin", mesh_size_min)
|
|
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", mesh_size_max)
|
|
gmsh.option.setNumber("Mesh.MshFileVersion", 2.2)
|
|
|
|
# 建立矽區主體 (Box)
|
|
silicon_vol = gmsh.model.occ.addBox(0, 0, 0, W_device, H_device, L_device)
|
|
|
|
# 建立 Emitter 和 Base 的接觸面 (Rectangles, 位在 Y = 0 的上表面)
|
|
# 由於 addRectangle 預設是在 X-Y 平面建立,我們需要將其旋轉 -90 度到 X-Z 平面
|
|
emitter_surf = gmsh.model.occ.addRectangle(
|
|
x_emit_center - 0.5 * w_emit, 0, z_emit_center - 0.5 * l_emit,
|
|
w_emit, l_emit
|
|
)
|
|
base_surf = gmsh.model.occ.addRectangle(
|
|
x_base_center - 0.5 * w_base, 0, z_base_center - 0.5 * l_base,
|
|
w_base, l_base
|
|
)
|
|
|
|
# 旋轉至 X-Z 平面 (繞起點,沿 X 軸方向向量 [1, 0, 0] 旋轉 pi/2)
|
|
gmsh.model.occ.rotate([(2, emitter_surf)], x_emit_center - 0.5 * w_emit, 0, z_emit_center - 0.5 * l_emit, 1, 0, 0, np.pi/2)
|
|
gmsh.model.occ.rotate([(2, base_surf)], x_base_center - 0.5 * w_base, 0, z_base_center - 0.5 * l_base, 1, 0, 0, np.pi/2)
|
|
|
|
# 使用布林片段 (Boolean Fragment) 將電極表面縫合嵌入到矽主體的頂面
|
|
# 這會確保網格在此交界處的節點能完美對齊
|
|
out, out_map = gmsh.model.occ.fragment(
|
|
[(3, silicon_vol)],
|
|
[(2, emitter_surf), (2, base_surf)]
|
|
)
|
|
gmsh.model.occ.synchronize()
|
|
|
|
# 找出各個實體 (Entity) 的 Tag 來指定 Physical Groups (區域與電極)
|
|
# 我們透過包圍盒 (Bounding Box) 來精確搜尋
|
|
# search format: xmin, ymin, zmin, xmax, ymax, zmax, dim
|
|
|
|
# 1. 找出 Silicon 3D 體積 (dim=3)
|
|
vol_entities = gmsh.model.getEntities(3)
|
|
silicon_tags = [tag for dim, tag in vol_entities]
|
|
gmsh.model.addPhysicalGroup(3, silicon_tags, tag=1, name="Silicon")
|
|
|
|
# 2. 找出 Emitter 接觸面 (dim=2, 位在 Y = 0)
|
|
eps = 0.1 * um
|
|
emitter_entities = gmsh.model.getEntitiesInBoundingBox(
|
|
x_emit_center - 0.5 * w_emit - eps, -eps, z_emit_center - 0.5 * l_emit - eps,
|
|
x_emit_center + 0.5 * w_emit + eps, eps, z_emit_center + 0.5 * l_emit + eps,
|
|
dim=2
|
|
)
|
|
emitter_tags = [tag for dim, tag in emitter_entities]
|
|
gmsh.model.addPhysicalGroup(2, emitter_tags, tag=10, name="emitter")
|
|
|
|
# 3. 找出 Base 接觸面 (dim=2, 位在 Y = 0)
|
|
base_entities = gmsh.model.getEntitiesInBoundingBox(
|
|
x_base_center - 0.5 * w_base - eps, -eps, z_base_center - 0.5 * l_base - eps,
|
|
x_base_center + 0.5 * w_base + eps, eps, z_base_center + 0.5 * l_base + eps,
|
|
dim=2
|
|
)
|
|
base_tags = [tag for dim, tag in base_entities]
|
|
gmsh.model.addPhysicalGroup(2, base_tags, tag=11, name="base")
|
|
|
|
# 4. 找出 Collector 接觸面 (整個底面, dim=2, 位在 Y = H_device)
|
|
collector_entities = gmsh.model.getEntitiesInBoundingBox(
|
|
-eps, H_device - eps, -eps,
|
|
W_device + eps, H_device + eps, L_device + eps,
|
|
dim=2
|
|
)
|
|
collector_tags = [tag for dim, tag in collector_entities]
|
|
gmsh.model.addPhysicalGroup(2, collector_tags, tag=12, name="collector")
|
|
|
|
# 生成 3D 網格
|
|
gmsh.model.mesh.generate(3)
|
|
msh_filename = "bjt_3d.msh"
|
|
gmsh.write(msh_filename)
|
|
gmsh.finalize()
|
|
print(f">>> Step 1 Completed: Mesh saved to {msh_filename}")
|
|
|
|
# =============================================================================
|
|
# 3. 載入 DEVSIM 並設定 3D 參數化摻雜 (Doping Profile)
|
|
# =============================================================================
|
|
print(">>> Step 2: Setting up 3D Doping Profiles in DEVSIM...")
|
|
device = "bjt_3d_device"
|
|
|
|
# 載入 Gmsh 產生的網格
|
|
create_gmsh_mesh(mesh=device, file=msh_filename)
|
|
add_gmsh_region(mesh=device, gmsh_name="Silicon", region="Silicon", material="Silicon")
|
|
for contact in ["collector", "emitter", "base"]:
|
|
add_gmsh_contact(mesh=device, gmsh_name=contact, region="Silicon", name=contact, material="metal")
|
|
finalize_mesh(mesh=device)
|
|
create_device(mesh=device, device=device)
|
|
|
|
# --- 定義 3D 擴散參數 (可自由調整數值進行參數化) ---
|
|
# Emitter (N+)
|
|
node_model(device=device, region="Silicon", name="emitter_doping", equation="1.0e19")
|
|
node_model(device=device, region="Silicon", name="emitter_depth", equation="0.8e-4") # 0.8 um 結深
|
|
node_model(device=device, region="Silicon", name="emitter_vdiff", equation="0.2e-4") # Y 垂直擴散係數
|
|
node_model(device=device, region="Silicon", name="emitter_hdiff", equation="0.15e-4") # X 橫向擴散係數
|
|
node_model(device=device, region="Silicon", name="emitter_zdiff", equation="0.15e-4") # Z 橫向擴散係數
|
|
|
|
# Base (P)
|
|
node_model(device=device, region="Silicon", name="base_doping", equation="1.0e17")
|
|
node_model(device=device, region="Silicon", name="base_depth", equation="3.5e-4") # 3.5 um 結深
|
|
node_model(device=device, region="Silicon", name="base_vdiff", equation="0.8e-4")
|
|
node_model(device=device, region="Silicon", name="base_hdiff", equation="0.6e-4")
|
|
node_model(device=device, region="Silicon", name="base_zdiff", equation="0.6e-4")
|
|
|
|
# Background Substrate (N-)
|
|
node_model(device=device, region="Silicon", name="nsub_doping", equation="1.0e16")
|
|
|
|
# --- 3D ERFC 摻雜分佈方程式 (X, Y, Z 三維空間分佈) ---
|
|
# Emitter 3D 摻雜 (Y 往下擴散,X 與 Z 則是雙向橫向擴散)
|
|
node_model(device=device, region="Silicon", name="nD_emit", equation=f"""
|
|
emitter_doping * erfc((y - emitter_depth) / emitter_vdiff)
|
|
* erfc(-(x + 0.5 * {w_emit} - {x_emit_center}) / emitter_hdiff)
|
|
* erfc((x - 0.5 * {w_emit} - {x_emit_center}) / emitter_hdiff)
|
|
* erfc(-(z + 0.5 * {l_emit} - {z_emit_center}) / emitter_zdiff)
|
|
* erfc((z - 0.5 * {l_emit} - {z_emit_center}) / emitter_zdiff)
|
|
""")
|
|
|
|
# Base 3D 摻雜
|
|
node_model(device=device, region="Silicon", name="nA_base", equation=f"""
|
|
base_doping * erfc((y - base_depth) / base_vdiff)
|
|
* erfc(-(x + 0.5 * {w_base} - {x_base_center}) / base_hdiff)
|
|
* erfc((x - 0.5 * {w_base} - {x_base_center}) / base_hdiff)
|
|
* erfc(-(z + 0.5 * {l_base} - {z_base_center}) / base_zdiff)
|
|
* erfc((z - 0.5 * {l_base} - {z_base_center}) / base_zdiff)
|
|
""")
|
|
|
|
# 合併總摻雜 (NetDoping)
|
|
node_model(device=device, region="Silicon", name="Donors", equation="nsub_doping + nD_emit")
|
|
node_model(device=device, region="Silicon", name="Acceptors", equation="1e10 + nA_base")
|
|
node_model(device=device, region="Silicon", name="NetDoping", equation="Donors - Acceptors")
|
|
|
|
# 建立 LogScale 變數,便於在 ParaView 中以對數範圍看濃度 (跨越 10^10 ~ 10^19)
|
|
node_model(device=device, region="Silicon", name="LogNetDoping", equation="asinh(NetDoping/2)/log(10)")
|
|
|
|
# =============================================================================
|
|
# 4. 輸出預覽檔案 (不進行求解)
|
|
# =============================================================================
|
|
preview_filename = "doping_3d_preview.tec"
|
|
print(f">>> Step 3: Exporting preview to {preview_filename}...")
|
|
write_devices(file=preview_filename, type="tecplot")
|
|
print(">>> Step 3 Completed! Ready for ParaView visualization.")
|