2021年9月18日 星期六

以Streamlit建立網頁GUI背景呼叫Nexxim.exe執行RC電路模擬

延續上一篇介紹的內容,本範例套用Streamlit模組,不到10行程式碼(st.XXX)便可以建立RC電路模擬GUI。使用者輸入R, C,按下執行模擬鍵,一秒便可以顯示S21分布圖。也可以從遠端設備登入網頁執行模擬工作。如圖二。

# -*- coding: utf-8 -*-
import os
import streamlit as st
import random
import matplotlib.pyplot as plt
from datetime import datetime



text = '''
.option PARHIER='local'
.option max_messages=1

* begin toplevel circuit

R1 Port1 Port2 {}
C2 0 Port2 {}
RPort1 Port1 0 PORTNUM=1 RZ=50 IZ=0
.PORT Port1 0 1 RPort1
RPort2 Port2 0 PORTNUM=2 RZ=50 IZ=0
.PORT Port2 0 2 RPort2

* end toplevel circuit
.LNA
+ LIN 10000 0 10000000000
+ FLAG='LNA'

.end'''


def readCSV(csv_name):
with open(csv_name) as f:
text = f.readlines()

freq = []
S11, S12, S21, S22 = [], [], [], []
for line in text[1:]:
_, f, s11_re, s11_im, s12_re, s12_im, s21_re, s21_im, s22_re, s22_im = line.split(',')
freq.append(float(f))
S11.append(complex(float(s11_re), float(s11_im)))
S12.append(complex(float(s12_re), float(s12_im)))
S21.append(complex(float(s21_re), float(s21_im)))
S22.append(complex(float(s22_re), float(s22_im)))
return freq, S21


st.header('RC電路S參數模擬')
R = st.number_input('電阻值(Ohm)')
C = st.number_input('電容值(pF)') * 1e-12
z = st.button('執行模擬')

n = datetime.now().timestamp()
if z:
with open('c:/demo/rc{}.cir'.format(n), 'w') as f:
f.write(text.format(R, C))

os.environ['path'] = 'C:\Program Files\AnsysEM\AnsysEM21.1\Win64'
os.system('nexxim c:/demo/rc{}.cir'.format(n))
os.system('sdf2csv c:/demo/rc{}.cir.sdf'.format(n))
with open('c:/demo/rc{}.cir.sdf.csv'.format(n)) as f:
text = f.readlines()

f, S21 = readCSV('c:/demo/rc{}.cir.sdf.csv'.format(n))
plt.figure(figsize=(4, 3))
plt.plot(f, [abs(i) for i in S21])
plt.grid()
plt.xlabel('freq(Hz)')
plt.ylabel('mag')
plt.xlim(0, 1e10)
plt.ylim(0, 1)
st.pyplot(plt)
print('Finished ID: {}'.format(n))
(圖一)網頁GUI

(圖二)由手機登入並執行模擬

2021年9月17日 星期五

如何用Streamlit設計網頁做為自動化程式介面

假設我們可以在網頁輸入參數,按下執行鍵,即可以讓遠端裝有EDA軟體的工作站執行模擬,並在模擬完成之後將結果回傳,這是不是很吸引人?只要區域網路內的任一台電腦,配備有瀏覽器,便可以透過網頁來執行模擬工作。這樣子的作法可以大大的提高EDA的使用效率,更可以讓更多不熟悉EDA操作的工程師也能正確的完成複雜的設定。

過往在開發網頁介面最大的困難是必須熟悉網頁框架,如Flask或Django。現在有了Streamlit,只要簡單的幾行程式碼便可以產生表單頁面,就算不熟悉HTML語法也可以輕鬆的完成介面的開發。這是一個相當新的技術,我將在之後的文章當中以實際例子來介紹,以下的程式碼是我們課堂練習過的計算機,用Streamlit模組只要下面幾行便可以生成頁面,可以遠端瀏覽器開啟使用。各位有興趣的可以先研究看看:

https://blog.jiatool.com/posts/streamlit/


import streamlit as st
from math import *

x = st.text_input('Simple Calculator', '1+1')
try:
st.write('Answer:', eval(x))
except:
'Invalid Formula!'


(圖一)網頁介面


2021年9月8日 星期三

如何背景執行電容Z11模擬並繪圖

當要檢查大量的電容Z11曲線,直接呼叫nexxim.exe執行AC模擬並繪圖會是一個比較有效率的做法。以下是處理單一一個電容的Python範例。加上迴圈可以一次處理大量的Z11計算及繪圖。

import os
import matplotlib.pyplot as plt
os.chdir(os.path.dirname(__file__))

netlist_template = r'''
.inc "{}"

x1 net_2 0 "{}"
I3 0 net_2 DC=0 AC 1 0

.LNA
+ 100000 100000000 10000
+ FLAG='LNA'

.print AC V(net_2)
.end'''

model_file = r'D:\demo\CL05A226MQ5QUN_Precise_DC0V_25degC_H.lib'
model_name = 'CL05A226MQ5QUN_Precise_DC0V_25degC'

netlist = netlist_template.format(model_file, model_name)
with open('./ZP.net', 'w') as f:
f.write(netlist)

os.environ['PATH'] = r'C:\Program Files\AnsysEM\AnsysEM21.2\Win64'
os.system("nexxim ZP.net")
os.system("sdf2csv ZP.net.sdf")

with open("ZP.net.sdf.csv") as f:
text = f.readlines()
data = []
for line in text[1:]:
try:
_, freq, Zre, Zim = line.strip().split(',')
data.append((float(freq), complex(float(Zre), float(Zim))))
except:
pass
freq, Z11 = zip(*data)
plt.plot(freq, [abs(i) for i in Z11])
plt.grid()
plt.show()
(圖一)Z11

(圖二)AEDT Designer的Z11


2021年8月24日 星期二

HFSS Layered Impedance計算

這裡透過遞迴完成計算,公式可以參考HFSS Help Layered Impedance章節。

from cmath import pi, sinh, cosh, tan, sqrt

u0 = 4 * pi * 1e-7
e0 = 8.854187817e-12

x = [(1, 1, 0, 0, 8670000, 1e-6),
(1, 1.00018, 0, 0, 1820000, 2e-6),
(4.4, 1, 0.02, 0, 0, 3e-6),
(1, 1, 0, 0, 0, 1e9)]

frequency = 1e9
w = 2 * pi * frequency
k0 = 2 * pi * frequency * sqrt(u0 * e0)

def calculate(x):
erk1, urk1, lte, ltm, sigmak, dk = x[0]

erk2 = -(sigmak) / (w * e0) - erk1 * lte
urk2 = -urk1 * ltm

erk = erk1 + 1j * erk2
urk = urk1 + 1j * urk2

Zwk = sqrt((u0 * urk) / (e0 * erk))

if len(x) > 1:
Zinput = calculate(x[1:])
rk = 1j * k0 * sqrt(erk * urk)
return Zwk * ((Zinput * cosh(rk * dk) + Zwk * sinh(rk * dk)) / (Zinput * sinh(rk * dk) + Zwk * cosh(rk * dk)))

else:
return Zwk
Z = calculate(x)

(圖一) 程式計算結果

(圖二) HFSS計算結果




2021年8月22日 星期日

如何讓IronPython呼叫Python輸出Base64字串並取得Base64字串

這裡的特點便是只透過記憶體傳遞資料,不須透過檔案讀寫便可以在IronPython及Python之間交換資料,可以字串形式傳遞資料或圖片。範例如下:

IronPython test1.py

import subprocess
import base64
import os
os.chdir(os.path.dirname(__file__))

os.environ['PATH'] = r'C:\Program Files\AnsysEM\AnsysEM20.2\Win64\commonfiles\CPython\3_7\winx64\Release\python'
proc = subprocess.Popen(['python', 'test2.py', '[1,2,3]', '[1,2,1]'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
base64_img = proc.communicate()[0]

print(base64_img)
base64_img_bytes = base64_img.encode('utf-8')
with open('plot.png', 'wb') as file_to_save:
decoded_image_data = base64.b64decode(base64_img_bytes)
file_to_save.write(decoded_image_data)

Python test2.py

import matplotlib.pyplot as plt
import io
import sys
x, y = eval(sys.argv[1]), eval(sys.argv[2])

import base64
my_stringIObytes = io.BytesIO()
plt.plot(x, y)
plt.savefig(my_stringIObytes, format='png')
my_stringIObytes.seek(0)
my_base64_jpgData = base64.b64encode(my_stringIObytes.read())
result = str(my_base64_jpgData, encoding = "utf-8")


2021年8月13日 星期五

如何在新版AEDT使用使用matplotlib庫

在AEDT2020R2當中,我們的腳本可以連結AEDT安裝目錄當中的matplotlib庫來繪製圖表以完成後處理自動化。但是在2021R1之後執行腳本卻會產生問題。原因是2021R1之後的matplotlib必須使用tcl8.6及tk8.6。而AEDT的Python Lib目錄底下卻沒有提供這兩個目錄。因此我們可以在電腦當中試著搜尋這兩個目錄,如果存在,將這兩個目錄複製到Lib目錄底下即可。



2021年8月5日 星期四

將RLC網表轉換至矩陣做AC電路求解

上一篇我介紹了如何從電路矩陣計算出頻率響應,這裡的例子介紹如何將網表(netlist)轉成矩陣。這個範例程式碼只能處理由Vsin, R, L, C所構成的任意電路。從這個例子我們可以知道如何找到足夠多的方程式來構成足以求解的矩陣。這個範例的未知變數共14個。因此矩陣大小為14x14。

AEDT 原理圖與AC頻率響應

# -*- coding: utf-8 -*-
"""
Created on Thu Aug 5 07:42:35 2021

@author: mlin
"""
from cmath import rect
from math import radians
import numpy as np
from numpy import pi
import copy
import matplotlib.pyplot as plt

netlist = '''
L2 net_1 net_6 1e-08
V4 net_1 0 SIN(0 0 1000000000 0 0 0) AC 1 0
R8 0 net_3 10
C11 net_4 net_6 1e-11
C14 net_6 net_1 1e-10
R17 0 net_4 10
L20 net_6 net_3 1e-08
C23 net_7 net_3 1e-11
L26 0 net_7 1e-08
'''

current = {}
nodes = {}
for i in netlist.splitlines():
if not i.strip():
continue
if i[0] == 'R':
cmp_name, node1, node2, resistance = i.split()
current[cmp_name] = (node1, node2, '-{}'.format(resistance))
elif i[0] == 'L':
cmp_name, node1, node2, inductance = i.split()
current[cmp_name] = (node1, node2, '-1j*w*{}'.format(inductance))
elif i[0] == 'C':
cmp_name, node1, node2, capacitance = i.split()
current[cmp_name] = (node1, node2, '-1/(1j*w*{})'.format(capacitance))
elif i[0] == 'V':
cmp_name, node1, node2, *_, mag, phase = i.split()
current[cmp_name] = (node1, node2, str(rect(float(mag), radians(float(phase)))))
else:
continue

for node, p in [(node1, '1'), (node2, '-1')]:
try:
nodes[node] += [(cmp_name, p)]
except:
nodes[node] = [(cmp_name, p)]

del (nodes['0'])
unknown_name = list(nodes.keys()) + list(current.keys())

unknown_array = []
for name in unknown_name:
if name[0] == 'V':
unknown_array.append(current[name][2])
else:
unknown_array.append('0')

matrix = []
for name1 in unknown_name:
row = ['0'] * len(unknown_name)
if name1 in nodes:
for branch, polarization in nodes[name1]:
row[unknown_name.index(branch)] = polarization
matrix.append(row)
else:
node1, node2, value = current[name1]
if name1[0] == 'V':
value = '0'
row[unknown_name.index(name1)] = value

if node1 in unknown_name:
row[unknown_name.index(node1)] = '1'
if node2 in unknown_name:
row[unknown_name.index(node2)] = '-1'
matrix.append(row)

result = []
evaluated_matrix = copy.deepcopy(matrix)
freq = np.arange(0.1, 1e9 + 1e6, 1e6)
for f in freq:
w = 2 * pi * f

for i in range(len(unknown_name)):
for j in range(len(unknown_name)):
evaluated_matrix[i][j] = eval(matrix[i][j])

A = np.array(evaluated_matrix)
B = np.array([eval(i) for i in unknown_array])
D = np.linalg.inv(A)
E = np.dot(D, B)
result.append(E)

result = list(zip(*result))
plt.plot(freq, [abs(i) for i in result[2]])
# plt.ylim(0, 1)
plt.grid()
plt.show()


Python程式碼計算出的頻率響應