import devsim import numpy as np import matplotlib.pyplot as plt import matplotlib.tri as tri from device_config import * device = "device_2d" # 1. Load the mesh devsim.create_gmsh_mesh(mesh=device, file="device_2d.msh") devsim.add_gmsh_region(mesh=device, gmsh_name="Silicon", region="Silicon", material="Silicon") devsim.add_gmsh_region(mesh=device, gmsh_name="Oxide", region="Oxide", material="Oxide") devsim.add_gmsh_region(mesh=device, gmsh_name="Molding", region="Molding", material="Molding") # Add contacts for Silicon region devsim.add_gmsh_contact(mesh=device, gmsh_name="MT1_Si", name="MT1_Si", region="Silicon", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MT2_Si", name="MT2_Si", region="Silicon", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_L_Si", name="MRING_L", region="Silicon", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_R_Si", name="MRING_R", region="Silicon", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="Substrate_Bottom", name="Substrate_Bottom", region="Silicon", material="metal") # Add contacts for Oxide region devsim.add_gmsh_contact(mesh=device, gmsh_name="MT1_Ox", name="MT1_Ox", region="Oxide", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MT2_Ox", name="MT2_Ox", region="Oxide", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_L_Ox", name="MRING_L_Ox", region="Oxide", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_R_Ox", name="MRING_R_Ox", region="Oxide", material="metal") # Add contacts for Molding region devsim.add_gmsh_contact(mesh=device, gmsh_name="MT1_Mold", name="MT1_Mold", region="Molding", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MT2_Mold", name="MT2_Mold", region="Molding", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_L_Mold", name="MRING_L_Mold", region="Molding", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="MRING_R_Mold", name="MRING_R_Mold", region="Molding", material="metal") devsim.add_gmsh_contact(mesh=device, gmsh_name="Substrate_Bottom_Mold", name="Substrate_Bottom_Mold", region="Molding", material="metal") # Add interfaces devsim.add_gmsh_interface(mesh=device, gmsh_name="Si_Ox_Interface", name="Si_Ox", region0="Silicon", region1="Oxide") devsim.add_gmsh_interface(mesh=device, gmsh_name="Ox_Mold_Interface", name="Ox_Mold", region0="Oxide", region1="Molding") devsim.add_gmsh_interface(mesh=device, gmsh_name="Si_Mold_Interface", name="Si_Mold", region0="Silicon", region1="Molding") devsim.finalize_mesh(mesh=device) devsim.create_device(mesh=device, device=device) # 2. Define Doping Profiles using sub-models to avoid long strings # Substrate (N-type) devsim.node_model(device=device, region="Silicon", name="nD_sub", equation=f"{N_SUB}") # Helper to generate 2D erfc profile string def get_erfc_expr(peak, x1, x2, hdiff, vdiff): return f"{peak} * erfc(y / {vdiff}) * 0.5 * (erf((x - ({x1})) / {hdiff}) - erf((x - ({x2})) / {hdiff}))" # P-well profiles (p11, p12, p13 on both sides) # p11 p11_left_expr = get_erfc_expr(P11_PEAK, -P11_X2, -P11_X1, P_WELL_HDDIFF, P_WELL_VDDIFF) p11_right_expr = get_erfc_expr(P11_PEAK, P11_X1, P11_X2, P_WELL_HDDIFF, P_WELL_VDDIFF) devsim.node_model(device=device, region="Silicon", name="nA_p11_l", equation=p11_left_expr) devsim.node_model(device=device, region="Silicon", name="nA_p11_r", equation=p11_right_expr) # p12 p12_left_expr = get_erfc_expr(P12_PEAK, -P12_X2, -P12_X1, P_WELL_HDDIFF, P_WELL_VDDIFF) p12_right_expr = get_erfc_expr(P12_PEAK, P12_X1, P12_X2, P_WELL_HDDIFF, P_WELL_VDDIFF) devsim.node_model(device=device, region="Silicon", name="nA_p12_l", equation=p12_left_expr) devsim.node_model(device=device, region="Silicon", name="nA_p12_r", equation=p12_right_expr) # p13 p13_left_expr = get_erfc_expr(P13_PEAK, -P13_X2, -P13_X1, P_WELL_HDDIFF, P_WELL_VDDIFF) p13_right_expr = get_erfc_expr(P13_PEAK, P13_X1, P13_X2, P_WELL_HDDIFF, P_WELL_VDDIFF) devsim.node_model(device=device, region="Silicon", name="nA_p13_l", equation=p13_left_expr) devsim.node_model(device=device, region="Silicon", name="nA_p13_r", equation=p13_right_expr) # N+ profiles nplus_left_expr = get_erfc_expr(NPLUS_PEAK, -NPLUS_X2, -NPLUS_X1, NPLUS_HDDIFF, NPLUS_VDDIFF) nplus_right_expr = get_erfc_expr(NPLUS_PEAK, NPLUS_X1, NPLUS_X2, NPLUS_HDDIFF, NPLUS_VDDIFF) devsim.node_model(device=device, region="Silicon", name="nD_nplus_l", equation=nplus_left_expr) devsim.node_model(device=device, region="Silicon", name="nD_nplus_r", equation=nplus_right_expr) # MRING N+ profiles mring_l_expr = get_erfc_expr(NPLUS_PEAK, -W_DEVICE, -MRING_X1, NPLUS_HDDIFF, NPLUS_VDDIFF) mring_r_expr = get_erfc_expr(NPLUS_PEAK, MRING_X1, W_DEVICE, NPLUS_HDDIFF, NPLUS_VDDIFF) devsim.node_model(device=device, region="Silicon", name="nD_mring_l", equation=mring_l_expr) devsim.node_model(device=device, region="Silicon", name="nD_mring_r", equation=mring_r_expr) # Combine into Donors and Acceptors devsim.node_model(device=device, region="Silicon", name="Donors", equation="nD_sub + nD_nplus_l + nD_nplus_r + nD_mring_l + nD_mring_r") devsim.node_model(device=device, region="Silicon", name="Acceptors", equation="1e10 + nA_p11_l + nA_p11_r + nA_p12_l + nA_p12_r + nA_p13_l + nA_p13_r") # NetDoping devsim.node_model(device=device, region="Silicon", name="NetDoping", equation="Donors - Acceptors") devsim.node_model(device=device, region="Silicon", name="LogNetDoping", equation="asinh(NetDoping / 2.0) / log(10.0)") devsim.node_model(device=device, region="Silicon", name="LogAcceptors", equation="log(Acceptors) / log(10.0)") # Write Tecplot output for ParaView devsim.write_devices(file="device_2d.tec", type="tecplot") devsim.write_devices(file="preview.tec", type="tecplot") print("Saved device_2d.tec and preview.tec") # 4. Generate a 2D Plot with Matplotlib to verify the doping profile print("Generating 2D plot...") x = devsim.get_node_model_values(device=device, region="Silicon", name="x") y = devsim.get_node_model_values(device=device, region="Silicon", name="y") net_dop = devsim.get_node_model_values(device=device, region="Silicon", name="NetDoping") log_dop = devsim.get_node_model_values(device=device, region="Silicon", name="LogNetDoping") elements = devsim.get_element_node_list(device=device, region="Silicon") # Convert elements into a format usable by matplotlib triangles = np.array(elements) # scale to micrometers for plotting x_um = np.array(x) / um y_um = np.array(y) / um log_acceptors = devsim.get_node_model_values(device=device, region="Silicon", name="LogAcceptors") def draw_oxide_and_metal(ax): # 0. Molding Region (light yellow-gray or beige) # Top Molding ax.add_patch(plt.Rectangle((-W_SIM / um, - (T_OX + H_MOLD) / um), 2 * W_SIM / um, H_MOLD / um, facecolor='#fbfcf7', edgecolor='lightgray', linewidth=0.5, alpha=0.9)) # Left Side Molding ax.add_patch(plt.Rectangle((-W_SIM / um, - T_OX / um), (W_SIM - W_DEVICE) / um, (H_SI + T_OX) / um, facecolor='#fbfcf7', edgecolor='lightgray', linewidth=0.5, alpha=0.9)) # Right Side Molding ax.add_patch(plt.Rectangle((W_DEVICE / um, - T_OX / um), (W_SIM - W_DEVICE) / um, (H_SI + T_OX) / um, facecolor='#fbfcf7', edgecolor='lightgray', linewidth=0.5, alpha=0.9)) # 1. Oxide Layer (light blue-gray) rect_oxide = plt.Rectangle((-W_DEVICE / um, - T_OX / um), 2 * W_DEVICE / um, T_OX / um, facecolor='#eaeef2', edgecolor='gray', linewidth=0.5, alpha=0.9) ax.add_patch(rect_oxide) # 2. Leadframe Island at bottom (dark grey-blue) rect_leadframe = plt.Rectangle((-W_SIM / um, H_SI / um), 2 * W_SIM / um, 15.0, facecolor='#34495e', edgecolor='black', alpha=0.9) ax.add_patch(rect_leadframe) # 3. Metal color & settings (grey color for electrodes) m_color = '#7f8c8d' # sleek dark gray m_edge = '#2c3e50' m_alpha = 1.0 # MT1 (Right side) # Long plate top part: X in [30, 186], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((30.0, -2.5), 156.0, 0.5, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Via 1 (under p11): X in [82.5, 92.5], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((82.5, -2.0), 10.0, 2.0, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Via 2 (under p13): X in [169.5, 179.5], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((169.5, -2.0), 10.0, 2.0, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Small plate top part: X in [250, 295], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((250.0, -2.5), 45.0, 0.5, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # MT2 (Left side) # Long plate top part: X in [-186, -30], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((-186.0, -2.5), 156.0, 0.5, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Via 1: X in [-92.5, -82.5], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((-92.5, -2.0), 10.0, 2.0, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Via 2: X in [-179.5, -169.5], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((-179.5, -2.0), 10.0, 2.0, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # Small plate top part: X in [-295, -250], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((-295.0, -2.5), 45.0, 0.5, facecolor=m_color, edgecolor=m_edge, alpha=m_alpha)) # MRING (Right & Left) mring_color = '#e67e22' # bright orange-red for MRING to distinguish mring_edge = '#d35400' # Right MRING via: X in [340, 356], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((340.0, -2.0), 16.0, 2.0, facecolor=mring_color, edgecolor=mring_edge, alpha=m_alpha)) # Right MRING top plate: X in [335, 356], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((335.0, -2.5), 21.0, 0.5, facecolor=mring_color, edgecolor=mring_edge, alpha=m_alpha)) # Left MRING via: X in [-356, -340], Y in [-2.0, 0] ax.add_patch(plt.Rectangle((-356.0, -2.0), 16.0, 2.0, facecolor=mring_color, edgecolor=mring_edge, alpha=m_alpha)) # Left MRING top plate: X in [-356, -335], Y in [-2.5, -2.0] ax.add_patch(plt.Rectangle((-356.0, -2.5), 21.0, 0.5, facecolor=mring_color, edgecolor=mring_edge, alpha=m_alpha)) # Add text labels ax.text(108.0, -2.8, 'MT1', color='black', fontsize=8, ha='center', weight='bold') ax.text(-108.0, -2.8, 'MT2', color='black', fontsize=8, ha='center', weight='bold') ax.text(348.0, -2.8, 'MRING', color='#d35400', fontsize=8, ha='center', weight='bold') ax.text(-348.0, -2.8, 'MRING', color='#d35400', fontsize=8, ha='center', weight='bold') ax.text(0, -50.0, 'Molding Region', color='darkgreen', fontsize=9, ha='center', va='center') ax.text(-406.0, 50.0, 'Molding\nCompound\n(Side)', color='darkgreen', fontsize=8, ha='center', va='center') ax.text(406.0, 50.0, 'Molding\nCompound\n(Side)', color='darkgreen', fontsize=8, ha='center', va='center') ax.text(0, -1.0, 'Oxide', color='blue', fontsize=9, ha='center', va='center') ax.text(0, H_SI/um + 7.5, 'Leadframe paddle (Island)', color='white', fontsize=9, ha='center', va='center', weight='bold') fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 12)) # Subplot 1: Net Doping tcf1 = ax1.tripcolor(x_um, y_um, triangles, log_dop, cmap='coolwarm', shading='flat') fig.colorbar(tcf1, ax=ax1, label='Log10(NetDoping) [asinh(N/2)/log(10)]') draw_oxide_and_metal(ax1) ax1.set_xlabel('X (μm)') ax1.set_ylabel('Y (μm)') ax1.set_title('2D Net Doping Profile (NetDoping = Donors - Acceptors)') ax1.set_xlim(-W_SIM / um, W_SIM / um) ax1.set_ylim(H_SI/um + 15.0, -110.0) # Show substrate, bottom contact, oxide, and top molding # Subplot 2: Acceptors (P-type dopants) tcf2 = ax2.tripcolor(x_um, y_um, triangles, np.array(log_acceptors), cmap='Purples', shading='flat') fig.colorbar(tcf2, ax=ax2, label='Log10(Acceptor Doping)') draw_oxide_and_metal(ax2) ax2.set_xlabel('X (μm)') ax2.set_ylabel('Y (μm)') ax2.set_title('2D Acceptor Doping Profile (p11, p12, p13)') ax2.set_xlim(-W_SIM / um, W_SIM / um) ax2.set_ylim(H_SI/um + 15.0, -110.0) plt.tight_layout() plt.savefig('doping_2d.png', dpi=300) plt.close() print("Plot saved to doping_2d.png")