Download this example

Download this example as a Jupyter Notebook or as a Python script.


Create a 5G Antenna Layout Component#

This example uses PyEDB to create a microstrip antenna array in EDB and then imports it into HFSS as a 3D layout component.

Prerequisites#

Perform imports#

[1]:
import os
import tempfile
import time

import ansys.aedt.core
import pyedb

Define constants#

These constants keep the example configuration in one place.

[2]:
AEDT_VERSION = "2026.1"
NG_MODE = False  # Set to ``True`` to run AEDT without the user interface.

Create a temporary directory#

Create a temporary working directory. The generated project files are stored in temp_folder.name.

Note: The final section removes the temporary folder. If you want to keep the AEDT project or intermediate data, copy them before running the cleanup step.

[3]:
temp_folder = tempfile.TemporaryDirectory(suffix=".ansys")

Build the EDB model#

Create helper classes for the geometry#

The Patch, Line, and LinearArray classes make it easier to define the array geometry before passing the polygon points to pyedb.Edb.

[4]:
class Patch:
    def __init__(self, width: float = 0.0, height: float = 0.0, position: float = 0.0):
        self.width = width
        self.height = height
        self.position = position

    @property
    def points(self):
        return [
            [self.position, -self.height / 2],
            [self.position + self.width, -self.height / 2],
            [self.position + self.width, self.height / 2],
            [self.position, self.height / 2],
        ]


class Line:
    def __init__(self, length=0.0, width=0.0, position=0.0):
        self.length = length
        self.width = width
        self.position = position

    @property
    def points(self):
        return [
            [self.position, -self.width / 2],
            [self.position + self.length, -self.width / 2],
            [self.position + self.length, self.width / 2],
            [self.position, self.width / 2],
        ]


class LinearArray:
    def __init__(self, nb_patch=1, array_length=10e-3, array_width=5e-3):
        self.nbpatch = nb_patch
        self.length = array_length
        self.width = array_width

    @property
    def points(self):
        return [
            [-1e-3, -self.width / 2 - 1e-3],
            [self.length + 1e-3, -self.width / 2 - 1e-3],
            [self.length + 1e-3, self.width / 2 + 1e-3],
            [-1e-3, self.width / 2 + 1e-3],
        ]

Launch EDB#

Use pyedb.Edb to create a new EDB project.

[5]:
aedb_path = os.path.join(temp_folder.name, "linear_array.aedb")

# Create the EDB database.
edb = pyedb.Edb(edbpath=aedb_path, version=AEDT_VERSION)
C:\actions-runner\_work\pyaedt-examples\pyaedt-examples\.venv\lib\site-packages\pyedb\generic\design_types.py:375: UserWarning: You are using PyEDB with grpc, which is currently in beta. Some feature might be missing or not working as expected. Please report any issue you find to the PyEDB team.
  warnings.warn(GRPC_BETA_WARNING, UserWarning)
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyEDB INFO: Using PyEDB with gRPC as Beta until ANSYS 2027R1 official release.
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyEDB INFO: Grpc session started
PyEDB INFO: Grpc session started: pid=10048
PyEDB INFO: RPC session acquired (open databases: 1)
PyEDB INFO: Refreshing the Components dictionary.
PyEDB INFO: EDB C:\Users\ansys\AppData\Local\Temp\tmpv9wg_ys8.ansys\linear_array.aedb created correctly.
PyEDB INFO: EDB initialized.

Define the stackup#

Add a custom high-conductivity copper material and then build the layer stack from top to bottom using the pyedb.Edb.stackup API.

[6]:
edb.materials.add_conductor_material(name="copper_high_cond", conductivity=60000000)
edb.materials.add_dielectric_material(name="Duroid (tm)", permittivity=2.2, dielectric_loss_tangent=0.0009)
[6]:
<pyedb.grpc.database.definition.materials.Material at 0x166bb1ea860>
[7]:
edb.stackup.add_layer_top(name="TOP",
                          layer_type="signal",
                          thickness="35um",
                          material="copper_high_cond")
edb.stackup.add_layer_below(name="Substrat",
                            base_layer_name="TOP",
                            layer_type="dielectric",
                            thickness="0.5mm",
                            material="Duroid (tm)")
edb.stackup.add_layer_below(name="GND",
                            base_layer_name="Substrat",
                            layer_type="signal",
                            thickness="35um",
                            material="copper")
[7]:
<ansys.edb.core.layer.stackup_layer.StackupLayer at 0x166bb1eb0a0>

Create the first patch and feed line#

Use the Patch and Line helpers to define the first radiating element and its feed line.

6642ce84b48240dda0d87f7b3c13e399

Define the geometry dimensions:

[8]:
w1 = 1.4e-3
h1 = 1.2e-3
initial_position = 0.0
l1 = 2.4e-3
trace_w = 0.3e-3

first_patch = Patch(width=w1, height=h1, position=initial_position)
edb.modeler.create_polygon(first_patch.points, layer_name="TOP", net_name="Array_antenna")
first_line = Line(length=l1, width=trace_w, position=first_patch.width)
edb.modeler.create_polygon(first_line.points, layer_name="TOP", net_name="Array_antenna")
[8]:
<pyedb.grpc.database.primitive.polygon.Polygon at 0x166bb1ebbb0>

Use the LinearArray helper to generate the remaining antenna elements.

[9]:
w2 = 2.29e-3
h2 = 3.3e-3
l2 = 1.9e-3
trace_w2 = 0.2e-3
rf_pin_location = [first_patch.width / 4.0, 0]

patch = Patch(width=w2, height=h2)
line = Line(length=l2, width=trace_w2)
linear_array = LinearArray(nb_patch=8, array_width=patch.height)

current_patch = 1
current_position = first_line.position + first_line.length

while current_patch <= linear_array.nbpatch:
    patch.position = current_position
    edb.modeler.create_polygon(patch.points, layer_name="TOP", net_name="Array_antenna")
    current_position = current_position + patch.width
    if current_patch < linear_array.nbpatch:
        line.position = current_position
        edb.modeler.create_polygon(line.points, layer_name="TOP", net_name="Array_antenna")
        current_position = current_position + line.length
    current_patch += 1

linear_array.length = current_position
[10]:
# Add the ground reference conductor.
edb.modeler.create_polygon(linear_array.points, layer_name="GND", net_name="GND")
[10]:
<pyedb.grpc.database.primitive.polygon.Polygon at 0x166bb1ea050>
[11]:
# Add point terminals that define the feed and its reference.
pos_term = edb.excitation_manager.create_point_terminal(
    x=rf_pin_location[0],
    y=rf_pin_location[1],
    layer="TOP",
    net="Array_antenna",
)
ref_term = edb.excitation_manager.create_point_terminal(
    x=rf_pin_location[0],
    y=rf_pin_location[1],
    layer="GND",
    net="GND",
)
pos_term.reference_terminal = ref_term

Display the layout.

[12]:
edb.nets.plot()
C:\actions-runner\_work\pyaedt-examples\pyaedt-examples\.venv\lib\site-packages\pyedb\common\nets.py:343: FutureWarning: Accessing deprecated property primitives_by_layer. use layout.primitives_by_layer property instead.
  prims_by_layers_dict = {i: j for i, j in self._pedb.modeler.primitives_by_layer.items()}
../../../_images/examples_high_frequency_antenna_5G_antenna_parametrics_21_1.png
PyEDB INFO: Plot Generation time 0.525
[12]:
(<Figure size 6000x3000 with 1 Axes>,
 <Axes: title={'center': 'Edb Top View Cell_XH8WVA'}>)

Save and close the EDB project before importing it into HFSS as a layout component.

[13]:
edb.save()
edb.close()
print(f"EDB saved to {aedb_path}. It is ready to be imported into AEDT.")
PyEDB INFO: RPC session released (open databases: 0)
EDB saved to C:\Users\ansys\AppData\Local\Temp\tmpv9wg_ys8.ansys\linear_array.aedb. It is ready to be imported into AEDT.

Open the layout component in HFSS#

Create an ansys.aedt.core.Hfss instance. When non_graphical=False, the AEDT user interface opens so you can monitor each step interactively.

[14]:
h3d = ansys.aedt.core.Hfss(
    project="Demo_3DComp",
    design="Linear_Array",
    version=AEDT_VERSION,
    new_desktop=True,
    non_graphical=NG_MODE,
    close_on_exit=True,
    solution_type="Terminal",
)
PyAEDT INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)].
PyAEDT INFO: PyAEDT version 1.0.dev0.
PyAEDT INFO: Initializing new Desktop session.
PyAEDT INFO: AEDT version 2026.1.
PyAEDT INFO: New AEDT session is starting on gRPC port 61084.
PyAEDT INFO: Starting new AEDT gRPC session on port 61084.
PyAEDT INFO: Launching AEDT server with gRPC transport mode: wnua
PyAEDT INFO: Electronics Desktop started on gRPC port 61084 after 10.2 seconds.
PyAEDT INFO: AEDT installation Path C:\Program Files\ANSYS Inc\v261\AnsysEM
PyAEDT INFO: Connected to AEDT gRPC session on port 61084.
PyAEDT INFO: Non-graphical mode detected. Disabling Desktop logs.
PyAEDT INFO: Project Demo_3DComp has been created.
PyAEDT INFO: Added design 'Linear_Array' of type HFSS.
PyAEDT INFO: AEDT objects correctly read

Set the model units to mm.

[15]:
h3d.modeler.model_units = "mm"
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec

Import the EDB as a 3D layout component#

The linear array can be inserted into the HFSS 3D Modeler. Combining layout components with 3D geometry is useful for mesh fusion and for simulating larger assemblies.

The following image shows the imported layout component in the HFSS 3D user interface.

a4998b19f09c42a5bad966107547b486

[16]:
component = h3d.modeler.insert_layout_component(aedb_path)
PyAEDT INFO: No EDB gRPC setting provided. Enabling gRPC for EDB.
PyAEDT INFO: Loading EDB with Grpc enabled.
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyEDB INFO: Using PyEDB with gRPC as Beta until ANSYS 2027R1 official release.
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
C:\actions-runner\_work\pyaedt-examples\pyaedt-examples\.venv\lib\site-packages\pyedb\generic\design_types.py:375: UserWarning: You are using PyEDB with grpc, which is currently in beta. Some feature might be missing or not working as expected. Please report any issue you find to the PyEDB team.
  warnings.warn(GRPC_BETA_WARNING, UserWarning)
PyEDB INFO: Grpc session started
PyEDB INFO: Grpc session started: pid=10696
PyEDB INFO: RPC session acquired (open databases: 1)
PyEDB INFO: Database linear_array.aedb Opened in 2026.1
PyEDB INFO: Cell Cell_XH8WVA Opened
PyEDB INFO: Refreshing the Components dictionary.
PyEDB INFO: Builder was initialized.
PyEDB INFO: EDB initialized.
PyEDB INFO: RPC session released (open databases: 0)
PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\pytest-of-ansys\pytest-2579\Demo_3DComp.aedt.
PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\pytest-of-ansys\pytest-2579\Demo_3DComp.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.01457524299621582
PyAEDT INFO: Loading EDB with Grpc enabled.
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyEDB INFO: Using PyEDB with gRPC as Beta until ANSYS 2027R1 official release.
PyEDB WARNING: AEDT project-related file C:\Users\ansys\AppData\Local\Temp\pytest-of-ansys\pytest-2579\Demo_3DComp.aedb\LayoutComponents\linear_array0\linear_array0.aedt.lock exists and may need to be deleted before opening the EDB in HFSS 3D Layout.
PyEDB INFO: Logger is initialized in EDB.
PyEDB INFO: legacy v0.75.0
PyEDB INFO: Python version 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
PyEDB INFO: Server already running on port 61123
PyEDB INFO: RPC session acquired (open databases: 1)
PyEDB INFO: Database linear_array0.aedb Opened in 2026.1
PyEDB INFO: Cell Cell_XH8WVA Opened
PyEDB INFO: Refreshing the Components dictionary.
PyEDB INFO: Builder was initialized.
PyEDB INFO: EDB initialized.

Assign a radiation boundary#

The 3D domain includes the air volume surrounding the antenna. This antenna is simulated from 20 GHz to 50 GHz.

Assign a radiation boundary to the outer faces of the air region. A common rule of thumb is to place this boundary about one quarter wavelength from the radiator:

\[\lambda / 4 = \frac{c_0}{4f} \approx 2.8\,\text{mm}\]
[17]:
h3d.modeler.fit_all()

h3d.modeler.create_air_region(2.8, 2.8, 2.8, 2.8, 2.8, 2.8, is_percentage=False)
h3d.assign_radiation_boundary_to_objects("Region")
PyAEDT INFO: Boundary Radiation Rad__VR66QS has been created.
[17]:
Rad__VR66QS

Set up the analysis#

HFSS adapts the finite element mesh iteratively. Use MaximumPasses to limit the number of adaptive refinements. The default convergence target is:

\[\max(|\Delta S|) < 0.02\]
[18]:
setup = h3d.create_setup()
setup.props["Frequency"] = "20GHz"
setup.props["MaximumPasses"] = 10

Define the frequency sweep.

[19]:
sweep1 = setup.add_sweep(name="20GHz_to_50GHz")
sweep1.props["RangeStart"] = "20GHz"
sweep1.props["RangeEnd"] = "50GHz"
sweep1.update()
[19]:
True

Solve the project.

[20]:
h3d.analyze()
PyAEDT INFO: Project Demo_3DComp Saved correctly
PyAEDT INFO: Solving all design setups. Analysis started...
PyAEDT INFO: Design setup None solved correctly in 0.0h 4.0m 7.0s
[20]:
True

Plot S-parameter results outside AEDT#

Plot the first available trace with Matplotlib.

[21]:
trace = h3d.get_traces_for_plot()
solution = h3d.post.get_solution_data(trace[0])
solution.plot()
PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\pytest-of-ansys\pytest-2579\Demo_3DComp.aedt.
PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\pytest-of-ansys\pytest-2579\Demo_3DComp.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.03587198257446289
PyAEDT INFO: PostProcessor class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: PostProcessor class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Solution Correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: Solution Correctly parsed. Elapsed time: 0m 0sec
[21]:
../../../_images/examples_high_frequency_antenna_5G_antenna_parametrics_39_1.png
../../../_images/examples_high_frequency_antenna_5G_antenna_parametrics_39_2.png

Create a 2D far-field report in AEDT#

Create a far-field report for the realized gain.

[22]:
variations = {}
variations["Freq"] = ["20GHz"]
variations["Theta"] = ["All"]
variations["Phi"] = ["All"]
h3d.insert_infinite_sphere(name="3D")

new_report = h3d.post.reports_by_category.far_field("db(RealizedGainTotal)", h3d.nominal_adaptive, "3D")
new_report.variations = variations
new_report.primary_sweep = "Theta"
new_report.create("Realized2D")
[22]:
True

Create a 3D far-field report in AEDT#

Reuse the same report object and switch it to a 3D polar plot.

[23]:
new_report.report_type = "3D Polar Plot"
new_report.secondary_sweep = "Phi"
new_report.create("Realized3D")
[23]:
True

Plot far-field data outside AEDT#

Retrieve the far-field data and plot it outside AEDT.

[24]:
solutions_custom = new_report.get_solution_data()
solutions_custom.plot_3d()
PyAEDT INFO: Solution Correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: Solution Correctly parsed. Elapsed time: 0m 0sec
[24]:
Class: ansys.aedt.core.visualization.plot.matplotlib.ReportPlotter
../../../_images/examples_high_frequency_antenna_5G_antenna_parametrics_45_2.png

Plot the electric field on selected nets and layers#

Create a field plot for the electric-field magnitude in AEDT.

[25]:
h3d.post.create_fieldplot_layers_nets(
    [["TOP", "Array_antenna"]],
    "Mag_E",
    intrinsics={"Freq": "20GHz", "Phase": "0deg"},
    plot_name="E_Layers",
)
PyAEDT INFO: Active Design set to Linear_Array
[25]:
Class: ansys.aedt.core.visualization.post.field_data.FieldPlot

Save and clean up#

Save the project#

[26]:
h3d.save_project(os.path.join(temp_folder.name, "test_layout.aedt"))
h3d.release_desktop()
# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.
time.sleep(3)
PyAEDT INFO: Project test_layout Saved correctly
PyEDB INFO: RPC session released (open databases: 0)
PyAEDT INFO: Desktop has been released and closed.

Clean up#

All generated project files are stored in temp_folder.name. If you are running this example as a notebook and want to keep those files, copy them before executing the cleanup step below.

[27]:
temp_folder.cleanup()

Download this example

Download this example as a Jupyter Notebook or as a Python script.