2021年4月28日 星期三

如何計算波形曲線通過特定電壓的時間點

在AEDT報告當中,如果要顯示波形通過特定電壓的所有時間點可以採用Y marker。但Y marker無法匯出時間點數值到CSV當中。一個解決方法是先按右鍵輸出.csv,再執行下面的script將所有時間點數值輸出到crossTime.csv。

# User Input
threshold = 0.5
wavform_path = 'D:/demo/Transient Voltage Plot 1.csv'

# Don't Change Code Below
import os
os.chdir(os.path.dirname(__file__))

with open(wavform_path) as f:
data = f.readlines()

time, voltage = data[0].split(',')

waveform = []
for i in data[1:]:
try:
t, v = i.strip().split(',')
waveform.append((float(t), float(v)))
except:
pass

crossT = []
for n, (t, v) in enumerate(waveform[:-1]):
if v == threshold:
crossT.append(t)
else:
t_1, v_1 = waveform[n+1]
if v < threshold < v_1 or v > threshold > v_1:
x = t + ((t_1-t)*abs((v-threshold)/(v_1-v)))
crossT.append(x)

with open('crossTime.csv', 'w') as f:
f.writelines(time + '\n')
f.writelines('\n'.join(map(str, crossT)))
(圖一)計算通過特定電壓的時間

2021年4月23日 星期五

在Q3D當中如何輸出self RLCG值

 # User Input

matrix_name = "Original"
frequency = 1000000000

# Don't Revise Code Below!-----------------------------------------------------
import os

os.chdir(os.path.dirname(__file__))

import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oDesign.ExportMatrixData("matrix.csv", "C, DC RL, AC RL", "", "Setup1:LastAdaptive", matrix_name, "ohm", "nH", "pF",
"mSie", frequency, "Maxwell,Spice,Couple", 0, True, 15, 20, 1)
Q3D_name = oDesign.GetName()

alltype = ['DCPlusAC Resistance Matrix',
'DCPlusAC Resistance Matrix Coupling Coefficient',
'Capacitance Matrix',
'Capacitance Matrix Coupling Coefficient',
'Spice Capacitance Matrix',
'Conductance Matrix',
'Conductance Matrix Coupling Coefficient',
'Spice Conductance Matrix',
'DC Inductance Matrix',
'DC Inductance Matrix Coupling Coefficient',
'DC Resistance Matrix',
'DC Resistance Matrix Coupling Coefficient',
'AC Inductance Matrix',
'AC Inductance Matrix Coupling Coefficient',
'AC Resistance Matrix',
'AC Resistance Matrix Coupling Coefficient', ]

group = {}
with open("matrix.csv") as f:
for i in f:
if len(i.strip()) == 0:
continue

if i.strip() in alltype:
mtype = i.strip()
group[mtype] = []
continue

try:
data = i.strip().split(',')
if data:
group[mtype].append(data)
except:
pass

result = {}
for mtype in group:
result[mtype] = []
for n, data in enumerate(group[mtype][1:]):
name = data[0]
values = data[1:]
result[mtype].append((name, values[n]))

with open('self.csv', 'w') as f:
f.writelines('Q3D Design: {}\n'.format(Q3D_name))
for mtype in result:
f.writelines('\n[{}]\n'.format(mtype))
for item, value in result[mtype]:
f.writelines('{:20} {:20}\n'.format(item, value))

AddWarningMessage('"self.csv" is saved!')

(圖一) 輸出self.csv的內容


2021年4月21日 星期三

如何將大文字檔(~GB)分割成多個小檔案

有時候模擬出來的資料檔過大,文字編輯器打不開或開啟過於緩慢,則可以將大檔案分割成小檔案以利操作。

# -*- coding: utf-8 -*-
"""
Created on Tue Apr 20 14:41:43 2021

@author: mlin
"""

big_file_path = 'd:/demo/0000_CPA_Sim_1.sp'
filesize_MB = 100

# Don't Change Code Below-------------------------------------
import os

N = filesize_MB * (2 ** 20)
n = 0
num = 1
data = []
output_dir = os.path.dirname(big_file_path)

with open(big_file_path) as fin:
name = '{}/part0.txt'.format(output_dir)
fout = open(name, 'w')
for i in fin:
if n / N > 1:
print(fout)
fout.close()

name = '{}/part{}.txt'.format(output_dir, num)
fout = open(name, 'w')

num += 1
n = 0

fout.writelines(i)
n += len(i)
(圖一) 分割檔案


2021年4月18日 星期日

如何在HFSS當中將物件以netname命名

從3D Layout匯出HFSS,HFSS物件的名稱為層數及編號。用net命名更加清楚,底下兩組腳本可以用來完成重新命名的工作。

在3D Layout執行,從3D Layout輸出座標訊息到rename.json檔案當中。 

Rename_Net_1.py

import ScriptEnv
import json, os
from collections import OrderedDict

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("Layout")
os.chdir(os.path.dirname(__file__))
db = {}
layerinfo = OrderedDict()
lower_elevation = 0
for layer in oEditor.GetStackupLayerNames()[::-1]:
info = oEditor.GetLayerInfo(layer)
layer_thickness = float(info[12].split(':')[1])

layerinfo[layer] = lower_elevation + layer_thickness / 2
lower_elevation += layer_thickness
db[layer] = []

for i in oEditor.FindObjects('Layer', layer):
otype = oEditor.GetPropertyValue('BaseElementTab', i, 'Type')
if otype in ['Pin', 'Via']:
location = oEditor.GetPropertyValue('BaseElementTab', i, 'Location')
elif otype in ['line', 'poly']:
location = oEditor.GetPropertyValue('BaseElementTab', i, 'Pt1')
else:
continue

x, y = location.split(',')
net = oEditor.GetPropertyValue('BaseElementTab', i, 'Net')
db[layer].append((x.strip(), y.strip(), net, otype))

with open('rename.json', 'w') as f:
json.dump((layerinfo, db), f, indent=4)
AddWarningMessage('Completed!')

在HFSS執行,讀取rename.json完成重新命名工作
Rename_Net_2.py
# ----------------------------------------------
# Script Recorded by ANSYS Electronics Desktop Version 2021.1.0
# 14:33:37 Apr 16, 2021
# ----------------------------------------------
import json, os
import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("3D Modeler")
os.chdir(os.path.dirname(__file__))
unit = oEditor.GetModelUnits()
with open('rename.json') as f:
layer_info, db = json.load(f)

result = {}
dielectric = []
for layer in db:
z = layer_info[layer]
for obj in db[layer]:
x, y, net, otype = obj
if net not in result:
result[net] = []

names = oEditor.GetBodyNamesByPosition(["NAME:Parameters",
"XPosition:=", "{}{}".format(x, unit),
"YPosition:=", "{}{}".format(y, unit),
"ZPosition:=", "{}{}".format(z * 1000, 'mm')
])

for name in names:
if name in dielectric:
continue
else:
dk = oEditor.GetPropertyValue('Geometry3DAttributeTab', name, 'Solve Inside')
if str(dk) == 'true':
dielectric.append(name)
elif name not in result[net]:
result[net].append(name)
else:
pass

# AddWarningMessage(str(result))

total = len(result)
n = 0
for net in result:
try:
n += 1
oDesktop.AddMessage(oProject.GetName(), oDesign.GetName(), 0, '{}/{}'.format(n, total))

oEditor.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:Geometry3DAttributeTab",
[
"NAME:PropServers"
] + result[net],
[
"NAME:ChangedProps",
[
"NAME:Name",
"Value:=" , ''.join(x if x.isalnum() else '_' for x in net)
]
]
]
])
except:
AddErrorMessage(str(result[name]))
oDesktop.AddMessage(oProject.GetName(), oDesign.GetName() ,0 ,'Completed!')

(圖一)以netname命名物件


2021年4月15日 星期四

如何插入編號遞增的HFSS設計並返回設計名稱

在project插入新的HFSS design需要設定design name,如果design name存在的話,程式則會中斷。以下函式呼叫可以遞增design編號的方式插入新的HFSS design。

import re
import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()


def insertHFSS(custom_name=None):
existing_designs = [i.GetName() for i in oProject.GetDesigns()]

if custom_name:
design_name = custom_name
else:
try:
oDesign = oProject.GetActiveDesign()
design_name = oDesign.GetName()
except:
design_name = 'HFSSDesign0'

m = re.search('(\d+)$', design_name)
if m:
N = int(m.group(1))
else:
design_name = design_name + str(0)
N = 0

while True:
design_name = re.sub('(\d+)$', str(N + 1), design_name)
if design_name not in existing_designs:
break
else:
N += 1

oProject.InsertDesign("HFSS", design_name, "DrivenTerminal", "")

return design_name


insertHFSS()

(圖一) 編號遞增插入設計


2021年4月12日 星期一

如何快速讀取兩個Q3D Matrix CSV並計算及輸出偏差值圖及CSV表

 

"""
Created on Thu Apr 8 11:16:35 2021
Output deviation of 2 Q3D matrix
@author: mlin
"""
# -------------------------------User Input-------------------------------------
path = 'd:/demo'
f_pair = [('case1.csv', 'case2.csv'), ]

# ---------------------------Don't Revise Code Below----------------------------
import os, math
import matplotlib.pyplot as plt

matrix_type = ['Capacitance Matrix',
'Conductance Matrix',
'DC Inductance Matrix',
'DC Resistance Matrix',
'AC Inductance Matrix',
'AC Resistance Matrix', ]


def getDiagonal(x):
N = int(math.sqrt(len(x)))
result = [x[i * N + i] for i in range(N)]
return result


def readQ3Dcsv(csv_path):
data = {i: [] for i in matrix_type}
name = {i: [] for i in matrix_type}

with open(csv_path) as f:
for line in f:
if line.strip() in matrix_type:
mtype = line.strip()
continue
else:
for i in line.strip().split(','):
try:
data[mtype].append(float(i))
continue
except:
...

try:
if i.strip() != '':
name[mtype].append(i)
except:
...

result = {i: getDiagonal(data[i]) for i in data}
return result, name


for f1, f2 in f_pair:
label = '{} - {}'.format(f1.split('.')[0], f2.split('.')[0])

f1 = os.path.join(path, f1)
f2 = os.path.join(path, f2)
data1, name1 = readQ3Dcsv(f1)
data2, name2 = readQ3Dcsv(f2)

with open(os.path.join(path, label + '.csv'), 'w') as f:
for mtype in data1:
result = []
f.writelines(f'\n[{mtype}]\n')
png_name = f'{label} [{mtype}]'
title = f'{label}\n[{mtype}]'
try:
for name, v1, v2 in zip(name1[mtype], data1[mtype], data2[mtype]):
deviation = (v2 - v1) * 100 / v1
result.append(deviation)
f.writelines(f'{name:36}{v2:12}{v1:12}{deviation:12.3f}%\n')
plt.plot(result, linewidth=1, color='b')
plt.title(title)
plt.ylim(-2, 2)
plt.grid()
plt.ylabel('deviation(%)')
plt.savefig(os.path.join(path, png_name + '.png'))
plt.show()
except:
print(f'Failed: {label}, {mtype}')

(圖一)差異圖

(圖二)差異表



2021年4月7日 星期三

將剪貼簿的內容輸出到netlist視窗當中

因為AEDT網表視窗沒有匯入及編輯功能,很難開發電路自動化工具。一個方法是將修改的網表填入Clipboard.SetText()當中,文字檔會送到剪貼簿保存起來。接著用CtrlV()觸發Ctrl+V按鍵事件將剪貼簿內容填入網表視窗。接著便可以執行電路模擬。

要模擬不同按鈕事件,按鈕編號可再以下連結查詢:

https://docs.microsoft.com/zh-tw/windows/win32/inputdev/virtual-key-codes?redirectedfrom=MSDN\

# ----------------------------------------------

# Script Recorded by ANSYS Electronics Desktop Version 2021.1.0
# 15:00:13 Apr 07, 2021
# ----------------------------------------------
import ScriptEnv
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()

import ctypes
from ctypes import wintypes
import time

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP = 0x0002
KEYEVENTF_UNICODE = 0x0004
KEYEVENTF_SCANCODE = 0x0008

MAPVK_VK_TO_VSC = 0

wintypes.ULONG_PTR = wintypes.WPARAM

class MOUSEINPUT(ctypes.Structure):
_fields_ = (("dx", wintypes.LONG),
("dy", wintypes.LONG),
("mouseData", wintypes.DWORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))

class KEYBDINPUT(ctypes.Structure):
_fields_ = (("wVk", wintypes.WORD),
("wScan", wintypes.WORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))

def __init__(self, *args, **kwds):
super(KEYBDINPUT, self).__init__(*args, **kwds)
# some programs use the scan code even if KEYEVENTF_SCANCODE
# isn't set in dwFflags, so attempt to map the correct code.
if not self.dwFlags & KEYEVENTF_UNICODE:
self.wScan = user32.MapVirtualKeyExW(self.wVk,
MAPVK_VK_TO_VSC, 0)

class HARDWAREINPUT(ctypes.Structure):
_fields_ = (("uMsg", wintypes.DWORD),
("wParamL", wintypes.WORD),
("wParamH", wintypes.WORD))

class INPUT(ctypes.Structure):
class _INPUT(ctypes.Union):
_fields_ = (("ki", KEYBDINPUT),
("mi", MOUSEINPUT),
("hi", HARDWAREINPUT))
_anonymous_ = ("_input",)
_fields_ = (("type", wintypes.DWORD),
("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
if result == 0:
raise ctypes.WinError(ctypes.get_last_error())
return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
LPINPUT, # pInputs
ctypes.c_int) # cbSize

# Functions

def PressKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode,
dwFlags=KEYEVENTF_KEYUP))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def CtrlV():
PressKey(0x11)
PressKey(0x56)
ReleaseKey(0x11)
ReleaseKey(0x56)
if __name__ =="__main__":
Clipboard.SetText('HelloWorld')
CtrlV()

(圖一) 執行腳本可輸出文字到netlist視窗當中