Files
tcad-devsim_triac/mos_transfer_temp_sweep.py

160 lines
6.2 KiB
Python

# mos_transfer_temp_sweep.py
# Parent script for Multi-Temperature MOSFET Transfer Sweep
# Runs mos_transfer_single_temp.py for various temperatures, combines data, and plots results
import os
import sys
import subprocess
import time
import numpy as np
import matplotlib.pyplot as plt
# Configuration
import argparse
parser = argparse.ArgumentParser(description="Run multi-temperature MOSFET transfer curves sweep.")
parser.add_argument("--vds", type=float, default=float(os.environ.get("VDS", 1.0)), help="Drain-Source Voltage Vds (V).")
args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else [])
vds_target = args.vds
DEV_DIR = os.environ.get("DEV_DIR", "devices/LDMOS")
default_out_dirname = f"output_transfer_temp_{time.strftime('%y%m%d')}_01"
OUT_DIR = os.path.join(os.environ.get("OUT_DIR", os.path.join(DEV_DIR, default_out_dirname)), "")
os.makedirs(OUT_DIR, exist_ok=True)
# Temperature list in Celsius
temps_C = [-40.0, -20.0, 0.0, 25.0, 50.0, 75.0, 100.0, 125.0]
print("=============================================================================")
# простые математические выражения, избегаем латекса в stdout
print("MOSFET Transfer Sweep across Temperatures")
print("=============================================================================")
print(f"Device directory: {DEV_DIR}")
print(f"Output directory: {OUT_DIR}")
print(f"Vds: {vds_target:.3f} V")
print(f"Temperatures (C): {temps_C}")
print("=============================================================================")
# Run simulation subprocesses
start_time = time.time()
for temp in temps_C:
print(f"\n>>> Running simulation at T = {temp:.1f} C...")
cmd = [
sys.executable,
"mos_transfer_single_temp.py",
"--temp", str(temp),
"--out_dir", OUT_DIR,
"--vds", str(vds_target)
]
# Set DEV_DIR env for child
child_env = os.environ.copy()
child_env["DEV_DIR"] = DEV_DIR
proc_start = time.time()
try:
# Run process and wait for completion
subprocess.run(cmd, env=child_env, check=True)
print(f"Finished T = {temp:.1f} C in {time.time() - proc_start:.1f}s.")
except subprocess.CalledProcessError as e:
print(f"Error: Simulation for T = {temp:.1f} C failed: {e}")
# Continue to other temperatures instead of hard-failing
print(f"\nAll simulations completed in {time.time() - start_time:.1f}s.")
# --- Combine Data and Plot ---
print("\nCombining data and generating plots...")
sweep_results = {}
for temp in temps_C:
csv_file = os.path.join(OUT_DIR, f"transfer_vgs_sweep_{temp:+.1f}C.csv")
if os.path.exists(csv_file):
try:
data = np.loadtxt(csv_file, delimiter=",", skiprows=1)
if data.ndim == 2 and data.shape[0] > 0:
vgs = data[:, 0]
ids = data[:, 1]
iav = data[:, 2]
sweep_results[temp] = (vgs, ids, iav)
else:
print(f"Warning: Empty or malformed data in {csv_file}")
except Exception as e:
print(f"Warning: Could not read {csv_file}: {e}")
if not sweep_results:
print("Error: No sweep results found to plot or combine. Aborting.")
sys.exit(1)
# Write combined CSV file
combined_csv_file = os.path.join(OUT_DIR, "mos_transfer_all_temps.csv")
with open(combined_csv_file, "w") as f_combined:
headers = []
for temp in sorted(sweep_results.keys()):
headers.append(f"Vgs_T_{temp:+.1f}C(V)")
headers.append(f"Ids_T_{temp:+.1f}C(A)")
headers.append(f"Iav_T_{temp:+.1f}C(A)")
f_combined.write(",".join(headers) + "\n")
# Find max length to align columns
max_len = max(len(v[0]) for v in sweep_results.values())
for idx in range(max_len):
row_cells = []
for temp in sorted(sweep_results.keys()):
vgs_vals, ids_vals, iav_vals = sweep_results[temp]
if idx < len(vgs_vals):
row_cells.append(f"{vgs_vals[idx]:.6e}")
row_cells.append(f"{ids_vals[idx]:.6e}")
row_cells.append(f"{iav_vals[idx]:.6e}")
else:
row_cells.append("")
row_cells.append("")
row_cells.append("")
f_combined.write(",".join(row_cells) + "\n")
print(f"Saved combined curves data to {combined_csv_file}")
# Set styling for premium looks
plt.rcParams.update({
'font.family': 'sans-serif',
'font.size': 11,
'grid.alpha': 0.3,
'grid.linestyle': '--'
})
# Generate distinct premium colors
colors = plt.cm.plasma(np.linspace(0.1, 0.9, len(sweep_results)))
# Plot 1: Linear Scale Plot (Ids in mA)
plt.figure(figsize=(10, 7))
for color_idx, temp in enumerate(sorted(sweep_results.keys())):
vgs_vals, ids_vals, _ = sweep_results[temp]
plt.plot(vgs_vals, ids_vals * 1e3, '-', color=colors[color_idx], linewidth=2, label=f"T = {temp:+.1f} °C")
plt.grid(True)
plt.xlabel("Gate-Source Voltage Vgs (V)", fontsize=12)
plt.ylabel("Drain-Source Current Ids (mA)", fontsize=12)
plt.title(f"LDMOS Transfer Characteristics vs Temperature (Linear Scale, Vds={vds_target:.3f}V)", fontsize=14, fontweight="bold")
plt.legend(loc="upper left", fontsize=10)
plt.tight_layout()
plot_path_linear = os.path.join(OUT_DIR, "mos_transfer_curves_linear.png")
plt.savefig(plot_path_linear, dpi=300)
plt.close()
print(f"Saved linear scale plot to {plot_path_linear}")
# Plot 2: Logarithmic Scale Plot (Ids in Amperes)
plt.figure(figsize=(10, 7))
for color_idx, temp in enumerate(sorted(sweep_results.keys())):
vgs_vals, ids_vals, _ = sweep_results[temp]
plt.plot(vgs_vals, np.abs(ids_vals), '-', color=colors[color_idx], linewidth=2, label=f"T = {temp:+.1f} °C")
plt.yscale('log')
plt.ylim(1e-12, 1e-1) # standard bounds for threshold inspection
plt.grid(True, which="both")
plt.xlabel("Gate-Source Voltage Vgs (V)", fontsize=12)
plt.ylabel("Drain-Source Current Ids (A)", fontsize=12)
plt.title(f"LDMOS Transfer Characteristics vs Temperature (Log Scale, Vds={vds_target:.3f}V)", fontsize=14, fontweight="bold")
plt.legend(loc="lower right", fontsize=10)
plt.tight_layout()
plot_path_log = os.path.join(OUT_DIR, "mos_transfer_curves_log.png")
plt.savefig(plot_path_log, dpi=300)
plt.close()
print(f"Saved log scale plot to {plot_path_log}")
print("\nAll tasks finished successfully!")