148 lines
6.0 KiB
Python
148 lines
6.0 KiB
Python
"""
|
|
opto_device.py - Unified Device Setup for Optocoupler Simulation
|
|
"""
|
|
import os
|
|
import sys
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from devsim import (
|
|
create_gmsh_mesh, add_gmsh_region, add_gmsh_interface, add_gmsh_contact,
|
|
finalize_mesh, create_device, get_region_list, get_interface_list,
|
|
set_parameter, get_node_model_values, get_edge_model_values,
|
|
solve, write_devices, element_from_edge_model
|
|
)
|
|
import opto_common
|
|
import opto_physics_model
|
|
|
|
|
|
class OptoDevice:
|
|
"""Unified device setup for optocoupler simulation."""
|
|
|
|
def __init__(self, name, dimension=2):
|
|
self.name = name
|
|
self.dimension = dimension
|
|
self.regions = []
|
|
self.mesh_file = None
|
|
|
|
def load_mesh(self, geo_file, mesh_file, force=False):
|
|
"""Generate and load mesh."""
|
|
opto_common.generate_mesh(geo_file, mesh_file, self.dimension, force)
|
|
self.mesh_file = mesh_file
|
|
print(f"--- Loading {self.dimension}D Mesh: {mesh_file} ---")
|
|
create_gmsh_mesh(mesh=self.name, file=mesh_file)
|
|
|
|
def setup_regions(self, region_material_map):
|
|
"""Add regions with material assignment."""
|
|
for region, material in region_material_map.items():
|
|
try:
|
|
add_gmsh_region(mesh=self.name, gmsh_name=region, region=region, material=material)
|
|
print(f" Region: {region}")
|
|
except Exception as e:
|
|
print(f" Warning: {region} - {e}")
|
|
|
|
def setup_interfaces(self, interface_list):
|
|
"""Add interfaces between regions."""
|
|
for iface_name, region0, region1 in interface_list:
|
|
try:
|
|
add_gmsh_interface(mesh=self.name, gmsh_name=iface_name, region0=region0, region1=region1, name=iface_name)
|
|
except:
|
|
pass
|
|
|
|
def setup_contacts(self, contact_list):
|
|
"""Add contacts for boundary conditions."""
|
|
for contact, region in contact_list:
|
|
try:
|
|
add_gmsh_contact(mesh=self.name, gmsh_name=contact, region=region, name=contact, material="metal")
|
|
print(f" Contact: {contact}")
|
|
except Exception as e:
|
|
print(f" Warning: {contact} - {e}")
|
|
|
|
def finalize(self):
|
|
"""Finalize mesh and create device."""
|
|
finalize_mesh(mesh=self.name)
|
|
create_device(mesh=self.name, device=self.name)
|
|
|
|
self.regions = list(get_region_list(device=self.name))
|
|
print(f"Regions: {self.regions}")
|
|
|
|
for region in self.regions:
|
|
eps_r = opto_common.PERMITTIVITY.get(region, 1.0)
|
|
set_parameter(device=self.name, region=region, name="Permittivity", value=eps_r * opto_common.eps_0)
|
|
|
|
opto_physics_model.setup_poisson(self.name, self.regions)
|
|
|
|
def setup_interface_physics(self):
|
|
"""Setup interface continuity conditions."""
|
|
interfaces = get_interface_list(device=self.name)
|
|
print(f"--- Interfaces: {len(interfaces)} ---")
|
|
for iface in interfaces:
|
|
try:
|
|
opto_physics_model.setup_interface(self.name, iface)
|
|
print(f" {iface} - OK")
|
|
except Exception as e:
|
|
print(f" {iface} - FAILED: {e}")
|
|
|
|
def apply_boundary_conditions(self, voltage_map):
|
|
"""Apply Dirichlet boundary conditions."""
|
|
print("--- Boundary Conditions ---")
|
|
for contact, voltage in voltage_map.items():
|
|
try:
|
|
opto_physics_model.setup_contact(self.name, contact, voltage)
|
|
print(f" {contact}: {voltage} V")
|
|
except Exception as e:
|
|
print(f" Warning: {contact} - {e}")
|
|
|
|
def apply_equipotential(self, region_voltage_map):
|
|
"""Apply equipotential constraints."""
|
|
print("--- Equipotential ---")
|
|
for region, (bias_name, voltage) in region_voltage_map.items():
|
|
try:
|
|
set_parameter(device=self.name, name=bias_name, value=voltage)
|
|
opto_physics_model.setup_virtual_contact(self.name, region, bias_name)
|
|
print(f" {region}: {voltage} V")
|
|
except Exception as e:
|
|
print(f" Warning: {region} - {e}")
|
|
|
|
def solve(self):
|
|
"""Solve Poisson equation."""
|
|
print(f"--- {self.dimension}D Poisson Solve ---")
|
|
solve(type="dc", absolute_error=1.0, relative_error=1e-10, maximum_iterations=30)
|
|
print(" Converged!")
|
|
|
|
def print_results(self):
|
|
"""Print simulation results."""
|
|
print("--- Results ---")
|
|
for region in self.regions:
|
|
try:
|
|
p = get_node_model_values(device=self.name, region=region, name="Potential")
|
|
if len(p) > 0:
|
|
print(f" {region}: {min(p):.1f} - {max(p):.1f} V")
|
|
except:
|
|
pass
|
|
|
|
print("--- E-field ---")
|
|
for region in ["region_dielectric", "region_led"]:
|
|
if region in self.regions:
|
|
try:
|
|
e = get_edge_model_values(device=self.name, region=region, name="ElectricField")
|
|
e_max = max(abs(min(e)), abs(max(e)))
|
|
print(f" {region}: E_max = {e_max:.0f} V/cm")
|
|
except:
|
|
pass
|
|
|
|
def create_element_models(self):
|
|
"""Create element models for 3D visualization."""
|
|
if self.dimension == 3:
|
|
print("--- 3D Element Models ---")
|
|
for region in self.regions:
|
|
try:
|
|
element_from_edge_model(device=self.name, region=region, edge_model="ElectricField")
|
|
except:
|
|
pass
|
|
|
|
def output(self, prefix):
|
|
"""Write output files."""
|
|
write_devices(file=f"{prefix}.msh", type="devsim")
|
|
write_devices(file=f"{prefix}.tec", type="tecplot")
|
|
print(f"Done: {prefix}.msh, {prefix}.tec")
|