Run an HFSS patch antenna workflow#
This example shows how to run a complete HFSS microstrip patch antenna workflow with PyAEDT-MCP tools. All steps were validated live against Ansys AEDT 2026 R1.
Note
Launch AEDT with non_graphical=False so the GUI is visible and you can
inspect the antenna geometry, solved mesh, and S11 report interactively in
the AEDT window.
Tool visibility model#
PyAEDT-MCP uses a connection-aware visibility model. The MCP client sees a small, focused tool surface first, and additional tools unlock after an AEDT session exists. This avoids “tool not available - connect first” round-trips and reduces token cost.
Stage |
Tools available |
|---|---|
Server start (no AEDT) |
|
After |
|
The tool set expands automatically as part of the launch or connect call
through await ctx.enable_components(tags={"requires_connection"}).
Workflow steps#
Check AEDT installation#
Tool: check_aedt_installed
Response:
AEDT is installed on this system.
Version: 2026.1
Executable: C:\Program Files\ANSYS Inc\v261\AnsysEM\ansysedt.exe
Launch AEDT#
Tool: launch_aedt
Parameter |
Value |
|---|---|
|
|
Launch in graphical mode so you can view the antenna and S11 report in the GUI.
Response:
Successfully launched AEDT
Version: 2026.1
gRPC Port: 59661
PID: 2600
Startup Time: ~18s
Create HFSS design#
Tool: create_design
Parameter |
Value |
|---|---|
|
|
|
|
Response:
Successfully created Hfss design
Design Name: PatchAntenna_Validation
Project: Project6
Solution Type: Terminal
Build geometry and boundaries#
Tool: run_python_code
from ansys.aedt.core import Hfss
hfss = Hfss(project=desktop.project_list[0], design="PatchAntenna_Validation", port=desktop.port)
# Create substrate (40x40x1.6 mm FR4)
substrate = hfss.modeler.create_box(
origin=[0, 0, 0],
sizes=["40mm", "40mm", "1.6mm"],
name="Substrate",
material="FR4_epoxy"
)
# Create ground plane
ground = hfss.modeler.create_rectangle(
orientation="XY",
origin=[0, 0, 0],
sizes=["40mm", "40mm"],
name="Ground"
)
# Create patch (24x24 mm centered on substrate)
patch = hfss.modeler.create_rectangle(
orientation="XY",
origin=["8mm", "8mm", "1.6mm"],
sizes=["24mm", "24mm"],
name="Patch"
)
# Assign PerfE boundaries
hfss.assign_perfecte_to_sheets(ground.name)
hfss.assign_perfecte_to_sheets(patch.name)
objects = [obj.name for obj in hfss.modeler.objects.values()]
result = "Geometry and boundaries created. Objects: {}".format(objects)
Response: Geometry and boundaries created. Objects: ['Substrate', 'Ground', 'Patch']
Add feed line, lumped port, and air box#
Tool: run_python_code
from ansys.aedt.core import Hfss
hfss = Hfss(project=desktop.project_list[0], design="PatchAntenna_Validation", port=desktop.port)
# Feed line (3mm wide, centered in X, from y=0 to patch edge at y=8mm)
feed_x = (40 - 3) / 2
feed = hfss.modeler.create_rectangle(
orientation="XY",
origin=["{:.1f}mm".format(feed_x), "0mm", "1.6mm"],
sizes=["3mm", "8mm"],
name="FeedLine"
)
hfss.assign_perfecte_to_sheets(feed.name)
# Feed port face (YZ plane at y=0, spans ground to feedline)
feed_port = hfss.modeler.create_rectangle(
orientation="YZ",
origin=["{:.1f}mm".format(feed_x), "0mm", "0mm"],
sizes=["1.6mm", "3mm"],
name="FeedPort"
)
# Lumped port with Ground reference
hfss.lumped_port(
assignment="FeedPort",
name="Port1",
reference=["Ground"],
integration_line=1
)
# Air box + radiation boundary
airbox = hfss.modeler.create_box(
origin=["-15mm", "-15mm", "-15mm"],
sizes=["70mm", "70mm", "31.6mm"],
name="AirBox",
material="vacuum"
)
hfss.assign_radiation_boundary_to_objects("AirBox")
objects = [obj.name for obj in hfss.modeler.objects.values()]
result = "Feed, port, and airbox added. Objects: {}".format(objects)
Response: Feed, port, and airbox added. Objects: ['FeedLine', 'Substrate', 'Ground', 'Patch', 'FeedPort', 'AirBox']
Create setup and frequency sweep#
Tool: run_python_code
from ansys.aedt.core import Hfss
hfss = Hfss(project=desktop.project_list[0], design="PatchAntenna_Validation", port=desktop.port)
# Create setup at 2.4 GHz
setup = hfss.create_setup(name="Setup1")
setup.props["Frequency"] = "2.4GHz"
setup.props["MaximumPasses"] = 6
setup.props["MaxDeltaS"] = 0.02
setup.update()
# Add linear step sweep 1-4 GHz
sweep = hfss.create_linear_step_sweep(
setup="Setup1",
unit="GHz",
start_frequency=1.0,
stop_frequency=4.0,
step_size=0.05,
name="Sweep1"
)
# Validate design
valid = hfss.validate_full_design()
result = "Setup and sweep created. Setup: {}, Sweep: Sweep1, Valid: {}".format(
setup.name, valid[1] if isinstance(valid, tuple) else valid
)
Response: Setup and sweep created. Setup: Setup1, Sweep: Sweep1, Valid: True
Capture pre-solve screenshot#
Tool: screenshot
The tool returns a PNG file of the AEDT viewport showing the antenna geometry.
Solve design#
Tool: run_python_code
import time
from ansys.aedt.core import Hfss
hfss = Hfss(project=desktop.project_list[0], design="PatchAntenna_Validation", port=desktop.port)
hfss.save_project()
t0 = time.time()
solved = hfss.analyze()
elapsed = time.time() - t0
result = "Solved: {}, Time: {:.0f}s".format(solved, elapsed)
Response: Solved: True, Time: 81s
Create S11 report and export results#
Tool: run_python_code
Because this workflow uses a terminal solution type, use St() notation
instead of S().
import math
import os
from ansys.aedt.core import Hfss
hfss = Hfss(project=desktop.project_list[0], design="PatchAntenna_Validation", port=desktop.port)
# Create S11 report via native API
report_module = hfss.odesign.GetModule("ReportSetup")
report_module.CreateReport(
"S11_Return_Loss",
"Terminal Solution Data",
"Rectangular Plot",
"Setup1 : Sweep1",
["Domain:=", "Sweep"],
["Freq:=", ["All"]],
["X Component:=", "Freq", "Y Component:=", ["dB(St(Port1_T1,Port1_T1))"]]
)
# Export Touchstone for data analysis
snp_path = r"D:\ANSYS-DEV\screenshots\PatchAntenna_Validation.s1p"
os.makedirs(os.path.dirname(snp_path), exist_ok=True)
hfss.export_touchstone(setup="Setup1", sweep="Sweep1", output_file=snp_path)
# Parse S1P (MA format: freq_GHz magnitude angle_deg)
with open(snp_path, "r") as f:
lines = f.readlines()
data = [l.strip() for l in lines if l.strip() and not l.startswith("!") and not l.startswith("#")]
min_s11, min_freq = 999, 0
for line in data:
parts = line.split()
if len(parts) >= 3:
freq, mag = float(parts[0]), float(parts[1])
db = 20 * math.log10(mag) if mag > 0 else -100
if db < min_s11:
min_s11, min_freq = db, freq
# Export report image
jpg_path = r"D:\ANSYS-DEV\screenshots\S11_Report_Validation.jpg"
report_module.ExportImageToFile("S11_Return_Loss", jpg_path, 1920, 1080)
result = "S11 Report created! Resonance: {:.4f} GHz, S11 = {:.2f} dB".format(min_freq, min_s11)
Response: S11 Report created! Resonance: 2.8000 GHz, S11 = -5.22 dB
Capture post-solve screenshot#
Tool: screenshot
The tool returns a PNG file that shows the S11_Return_Loss report in the AEDT
GUI.
Clear AEDT (optional)#
Tool: clear_aedt
Response: AEDT state cleared. Closed 1 project(s).