【ChatGPT】オープンソースの特定ライブラリのPythonスクリプト作るのに便利かも。。OpenEMSのXMLを描画【FreeCAD】

ChatGPT

ChatGPTでいろいろ遊んでいます。なかなかに面白い嘘をつくのと、あんまり専門的でないので、僕の専門の用途では、なかなかうまく回答を引き出せないのですが(笑)

使いようによっては、かなり楽な部分がいろいろでてきたので。。ひとつ紹介。

オープンソースのライブラリなどを使い始めるときとかコーディングをするのに、とっても便利なんじゃないかな?と。。時々とんでもないの返してくるけど、そこそこ使われているようなものは、かなりちゃんとしたのが返ってきます。

今回は、OpenEMSが吐き出すXMLファイルを、FreeCADで描画するための部分部分で作っていく例を少し書いてみます。

ちなみに、ChatGPT 4使っています。3.5でもええような気もしますが。。OpenAIは、1か月毎で、入ったり出たりできるので、使うときだけ契約するのでもいいのかも?

FreeCADのPython=マクロ

FreeCADでPythonを動かすのに、Pythonコンソールを使うのもいいのですが、マクロを作成して、そこで実行すると簡単です。メニュー>マクロ から マクロの実行画面で、作成を押すと、Pythonスクリプトが書けます。 ここからいろいろ図形かいたりできます。

ChatGPTで聞いてみる

最初に、お決まりのあなたは、FreeCAD とPythonのエキスパートです!って言っておきます。

そうすると、なんでも聞いてくれ!ってかえってきます。 上は、新規の図面を作って、そこにBOXを書いてくれるスクリプトですね。 これもちゃんと動きます。

やりたいことを書いていく(GUI)

FreeCAD ってGUIにはQtを使っているようで、GUIを書くときに、Qtの関数名(API)を覚えておいて書かないといけないのですが、たまにしか使わないので、そんなの覚えてません。が。。

Filenameを選択するGUIをFreeCADのPythonで書いてくれって書くと。。

PySideを使って書いてくれます。

この関数は、現在のディレクトリから始まり、XML ファイルのみを表示するファイル ダイアログを開きます。 ファイルを選択すると、そのファイルへのパスが出力されます。 上記のスクリプトを実行するには、FreeCAD の GUI が必要であるため、FreeCAD の Python コンソールまたはスクリプト エディタで実行する必要があることに注意してください。 FreeCAD の外部のスタンドアロン Python インタープリターで実行しても機能しません。

ってちゃんとFreeCADのPythonコンソールか スクリプトエディタか使えと。。

これを先ほどのマクロにコピペして実行すると、ちゃんと動きます。

FreeCADの機能を駆使して描いてくれる。。

読み込みたいXMLをべた貼りして、これを書きたいんだよ。って無茶ぶりしてみると。。

それXMLみたいだね。 Cylinderはあるけど、Cylindrical shellってないから、引き算で作るとできるよ。と。。

こんなの書いてくれます。

で、これを実行すると。。 全部FreeCADで書き出してくれました。

ちなみに、これは、OpenEMSのmatlab での同軸のモデルです。

https://github.com/thliebig/openEMS/blob/master/matlab/examples/waveguide/Coax.m

これが吐き出す xmlファイルを見せてくれているはずです。。が。。実はちょっと違いますw

Octave & OpenEMSでCoax.mの、この部分

 CSXGeomPlot([Sim_Path '/' Sim_CSX]);

を動かすと、一応、CSXCADで確認できます。

また、Paraviewで見たい場合は、VTKで打ち出すオプションをつけると。。すべての

openEMS_opts = '--debug-material --debug-PEC --debug-operator --debug-boxes';
RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts);

こんな風に確認できます。

違いますよね。。

Cylindrical shellの半径方向が違っていたり。。なのかな?

その違いを言ってあげると、ChatGPTは、返答してくれて。。書き出してくれます。

FreeCADのPythonスニペットとして使えそうですね!

ChatGPTが、今回使えそうなスニペット書いてくれたので一応、ここに保存(笑)

#
# Defs for create object by Coordinate Values
#


import FreeCAD, Part

doc = FreeCAD.newDocument() # Creates a new document

def create_cylinder(x1, y1, z1, x2, y2, z2, radius):
cylinder = doc.addObject("Part::Cylinder", "Cylinder")
cylinder.Radius = radius
cylinder.Height = z2 - z1 # Assuming z2 is the top of the cylinder
cylinder.Placement = FreeCAD.Placement(FreeCAD.Vector(x1, y1, z1), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
return cylinder


def create_shell(x1, y1, z1, x2, y2, z2, radius, shell_width):
outer_cylinder = create_cylinder(x1, y1, z1, x2, y2, z2, radius + shell_width)
inner_cylinder = create_cylinder(x1, y1, z1, x2, y2, z2, radius)
shell = doc.addObject("Part::Cut", "Shell")
shell.Base = outer_cylinder
shell.Tool = inner_cylinder
return shell

def create_box(x1, y1, z1, x2, y2, z2):
box = doc.addObject("Part::Box", "Box")
box.Length = abs(x2 - x1)
box.Width = abs(y2 - y1)
box.Height = abs(z2 - z1)
box.Placement = FreeCAD.Placement(FreeCAD.Vector(min(x1, x2), min(y1, y2), min(z1, z2)), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
return box

#
# XML parse and 
#

import xml.etree.ElementTree as ET

# Parse the XML file
tree = ET.parse('your_file.xml')
root = tree.getroot()

# Iterate over the XML elements
for element in root.findall('.//Primitives'):
for primitive in element:
if primitive.tag == 'Cylinder':
radius = float(primitive.get('Radius'))
p1 = primitive.find('P1')
p2 = primitive.find('P2')
x1, y1, z1 = float(p1.get('X')), float(p1.get('Y')), float(p1.get('Z'))
x2, y2, z2 = float(p2.get('X')), float(p2.get('Y')), float(p2.get('Z'))
create_cylinder(x1, y1, z1, x2, y2, z2, radius)

elif primitive.tag == 'CylindricalShell':
radius = float(primitive.get('Radius'))
shell_width = float(primitive.get('ShellWidth'))
p1 = primitive.find('P1')
p2 = primitive.find('P2')
x1, y1, z1 = float(p1.get('X')), float(p1.get('Y')), float(p1.get('Z'))
x2, y2, z2 = float(p2.get('X')), float(p2.get('Y')), float(p2.get('Z'))
create_shell(x1, y1, z1, x2, y2, z2, radius, shell_width)


for element in root.findall('.//Primitives'):
for primitive in element:
# ... (existing code for Cylinder and CylindricalShell) ...

elif primitive.tag == 'Box':
p1 = primitive.find('P1')
p2 = primitive.find('P2')
x1, y1, z1 = float(p1.get('X')), float(p1.get('Y')), float(p1.get('Z'))
x2, y2, z2 = float(p2.get('X')), float(p2.get('Y')), float(p2.get('Z'))
create_box(x1, y1, z1, x2, y2, z2)

for element in root.findall('.//Excitation'):
# For now, just print the Excitation details
print("Excitation:")
print("Name: " + element.get('Name'))
print("Type: " + element.get('Type'))
print("Excite: " + element.get('Excite'))
print()

for element in root.findall('.//DumpBox'):
# For now, just print the DumpBox details
print("DumpBox:")
print("Name: " + element.get('Name'))
print("DumpMode: " + element.get('DumpMode'))
print()


#
# File Selection with GUI
#

from PySide import QtGui

def select_file():
filename, _ = QtGui.QFileDialog.getOpenFileName(None, "Open XML File", "", "XML Files (*.xml)")
return filename

filename = select_file()

if filename:
print(f"Selected file: {filename}")
else:
print("No file selected")

便利かも。。デバッグしながら、一緒に書いていくと、ちゃんと書けるようになる。。

 

関係ないけど。。FDTDシミュレーションの結果

ちなみに。。OpenEMSで、矩形グリッドで切るのと、Cylindricalグリッドで切って、計算させると、やはり若干の違いがでてきますね。。

 

投稿者 tom2rd

コメントを残していただけるとありがたいです

Loading Facebook Comments ...

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください