目次
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グリッドで切って、計算させると、やはり若干の違いがでてきますね。。
OpenEMS 同軸 矩形グリッド pic.twitter.com/oivSI1iiIO
— Tetsuya Tominaga (@tom2rd) May 21, 2023

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