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視窗當中


2021年4月5日 星期一

將HFSS個別輸出的遠場圖更名為port的名稱

import os

path = 'd:/demo_FFD'

with open(os.path.join(path, 'exportelement.txt')) as f:
text = f.readlines()

for line in text[1:]:
try:
port_name, file_name = line.strip().split()
port_name = port_name.split(':')[0]
os.rename('{}/{}.ffd'.format(path, file_name),
'{}/{}.ffd'.format(path, port_name))
except:
pass


2021年4月4日 星期日

一次畫出IBIS當中所有[Model]的[Rising Waveform]跟[Falling Waveform]

 指定IBIS檔案路徑,執行以下腳本即可。

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 4 09:23:17 2021

@author: mlin
"""
import matplotlib.pyplot as plt


def plotWaveform(model_name, waveform):
plt.figure(figsize=(7, 4))
plt.clf()
header = []
_time_, _typ_, _min_, _max_ = [], [], [], []
for i in waveform:
if '=' in i:
header.append(i.strip())
continue
if '|' in i:
continue
try:
_time, _typ, _min, _max = i.strip().split()
_time_.append(float(_time.lower().replace('ps', 'e-12').replace('ns', 'e-9').replace('us', 'e-6')))
_typ_.append(
float(_typ.lower().replace('nv', 'e-9').replace('uv', 'e-6').replace('mv', 'e-3').replace('v', '')))
_min_.append(
float(_min.lower().replace('nv', 'e-9').replace('uv', 'e-6').replace('mv', 'e-3').replace('v', '')))
_max_.append(
float(_max.lower().replace('nv', 'e-9').replace('uv', 'e-6').replace('mv', 'e-3').replace('v', '')))
except:
pass
plt.plot(_time_, _typ_, 'g')
plt.plot(_time_, _min_, 'b')
plt.plot(_time_, _max_, 'r')
plt.title('{}\n{}\n{}\n{}'.format(model_name, ';'.join(header[0:3]), ';'.join(header[3:6]), ';'.join(header[6:9])))
plt.xlabel('Time(sec)')
plt.ylabel('Waveform(v)')
plt.grid()
plt.show()


data = {}
container = None
with open('D:\OneDrive - ANSYS, Inc/Models/IBIS/tpz015lg.ibs') as f:
for i in f:
if i.startswith('[Model]'):
_, model_name = i.strip().split()
data[model_name] = {}
continue

if i.startswith('['):
container = None

if '[rising waveform]' in i.lower():
try:
data[model_name]['rising_waveform'].append([])
except:
data[model_name]['rising_waveform'] = [[]]
finally:
container = data[model_name]['rising_waveform'][-1]
continue

if '[falling waveform]' in i.lower():
try:
data[model_name]['falling_waveform'].append([])
except:
data[model_name]['falling_waveform'] = [[]]
finally:
container = data[model_name]['falling_waveform'][-1]
continue

try:
container.append(i)
except:
pass

for model in data:
try:
for i in data[model]['rising_waveform']:
plotWaveform(model, i)
except:
pass
for model in data:
try:
for i in data[model]['falling_waveform']:
plotWaveform(model, i)
except:
pass
[圖一]畫出所有[Model]的[Rising Waveform]跟[Falling Waveform]