Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. SPDX-License-Identifier: MIT
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
EDB: 5G linear array antenna#
This example shows how you can use HFSS 3D Layout to create and solve a 5G linear array antenna.
Perform required imports#
Perform required imports.
[1]:
import os
import tempfile
[2]:
from ansys.aedt.core import Hfss3dLayout
[3]:
import pyedb
from pyedb.generic.general_methods import generate_unique_name
Set non-graphical mode#
Set non-graphical mode. The default is False
.
[4]:
non_graphical = False
[5]:
class Patch:
def __init__(self, width=0.0, height=0.0, position=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],
]
[6]:
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],
]
[7]:
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],
]
[8]:
tmpfold = tempfile.gettempdir()
aedb_path = os.path.join(tmpfold, generate_unique_name("pcb") + ".aedb")
print(aedb_path)
edb = pyedb.Edb(edbpath=aedb_path, edbversion="2024.2")
C:\Users\ansys\AppData\Local\Temp\pcb_NAYBNG.aedb
PyAEDT INFO: Logger is initialized in EDB.
PyAEDT INFO: legacy v0.34.2
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: EDB C:\Users\ansys\AppData\Local\Temp\pcb_NAYBNG.aedb created correctly.
PyAEDT INFO: EDB initialized.
[9]:
# ## Add stackup layers
#
# Add the stackup layers.
layers = {
"TOP": {"type": "signal", "thicness": "35um", "material": "copper"},
"Substrat": {"type": "dielectric", "thicness": "0.5mm", "material": "Duroid (tm)"},
"GND": {"type": "signal", "thicness": "35um", "material": "copper"},
"Gap": {"type": "dielectric", "thicness": "0.05mm", "material": "Air"},
"Virt_GND": {"type": "signal", "thicness": "35um", "material": "copper"},
}
[10]:
edb.stackup.load(layers)
PyAEDT INFO: Configuration file does not have material definition. Using aedb and syslib materials.
[10]:
True
Create linear array#
Create the first patch of the linear array.
[11]:
first_patch = Patch(width=1.4e-3, height=1.2e-3, position=0.0)
edb.modeler.create_polygon(first_patch.points, "TOP", net_name="Array_antenna")
# First line
first_line = Line(length=2.4e-3, width=0.3e-3, position=first_patch.width)
edb.modeler.create_polygon(first_line.points, "TOP", net_name="Array_antenna")
[11]:
<pyedb.dotnet.edb_core.edb_data.primitives_data.EdbPolygon at 0x21ec0e0b400>
Patch linear array#
Patch the linear array.
[12]:
patch = Patch(width=2.29e-3, height=3.3e-3)
line = Line(length=1.9e-3, width=0.2e-3)
linear_array = LinearArray(nb_patch=8, array_width=patch.height)
[13]:
current_patch = 1
current_position = first_line.position + first_line.length
[14]:
while current_patch <= linear_array.nbpatch:
patch.position = current_position
edb.modeler.create_polygon(patch.points, "TOP", net_name="Array_antenna")
current_position += patch.width
if current_patch < linear_array.nbpatch:
line.position = current_position
edb.modeler.create_polygon(line.points, "TOP", net_name="Array_antenna")
current_position += line.length
current_patch += 1
[15]:
linear_array.length = current_position
Add ground#
Add a ground.
[16]:
edb.modeler.create_polygon(linear_array.points, "GND", net_name="GND")
[16]:
<pyedb.dotnet.edb_core.edb_data.primitives_data.EdbPolygon at 0x21ec0e8c2e0>
Add connector pin#
Add a central connector pin.
[17]:
edb.padstacks.create(padstackname="Connector_pin", holediam="100um", paddiam="0", antipaddiam="200um")
con_pin = edb.padstacks.place(
[first_patch.width / 4, 0],
"Connector_pin",
net_name="Array_antenna",
fromlayer="TOP",
tolayer="GND",
via_name="coax",
)
PyAEDT INFO: Padstack Connector_pin create correctly
Add connector ground#
Add a connector ground.
[18]:
edb.modeler.create_polygon(first_patch.points, "Virt_GND", net_name="GND")
edb.padstacks.create("gnd_via", "100um", "0", "0")
con_ref1 = edb.padstacks.place(
[first_patch.points[0][0] + 0.2e-3, first_patch.points[0][1] + 0.2e-3],
"gnd_via",
fromlayer="GND",
tolayer="Virt_GND",
net_name="GND",
)
con_ref2 = edb.padstacks.place(
[first_patch.points[1][0] - 0.2e-3, first_patch.points[1][1] + 0.2e-3],
"gnd_via",
fromlayer="GND",
tolayer="Virt_GND",
net_name="GND",
)
con_ref3 = edb.padstacks.place(
[first_patch.points[2][0] - 0.2e-3, first_patch.points[2][1] - 0.2e-3],
"gnd_via",
fromlayer="GND",
tolayer="Virt_GND",
net_name="GND",
)
con_ref4 = edb.padstacks.place(
[first_patch.points[3][0] + 0.2e-3, first_patch.points[3][1] - 0.2e-3],
"gnd_via",
fromlayer="GND",
tolayer="Virt_GND",
net_name="GND",
)
PyAEDT INFO: Padstack gnd_via create correctly
Add excitation port#
Add an excitation port.
[19]:
edb.padstacks.set_solderball(con_pin, "Virt_GND", isTopPlaced=False, ballDiam=0.1e-3)
port_name = edb.padstacks.create_coax_port(con_pin)
Plot geometry#
Plot the geometry.
[20]:
edb.nets.plot(None)
PyAEDT INFO: Plot Generation time 0.109
[20]:
(<Figure size 6000x3000 with 1 Axes>, <Axes: title={'center': 'Edb Top View'}>)
Save and close Edb instance prior to opening it in Electronics Desktop.#
Save EDB.
[21]:
edb.save_edb()
edb.close_edb()
print("EDB saved correctly to {}. You can import in AEDT.".format(aedb_path))
PyAEDT INFO: EDB file save time: 0.00ms
PyAEDT INFO: EDB file release time: 0.00ms
EDB saved correctly to C:\Users\ansys\AppData\Local\Temp\pcb_NAYBNG.aedb. You can import in AEDT.
Launch HFSS 3D Layout and open EDB#
Launch HFSS 3D Layout and open EDB.
[22]:
h3d = Hfss3dLayout(
projectname=aedb_path, specified_version="2024.2", new_desktop_session=True, non_graphical=non_graphical
)
PyAEDT WARNING: Argument `projectname` is deprecated for method `__init__`; use `project` instead.
PyAEDT WARNING: Argument `specified_version` is deprecated for method `__init__`; use `version` instead.
PyAEDT WARNING: Argument `new_desktop_session` is deprecated for method `__init__`; use `new_desktop` instead.
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 0.12.dev0.
PyAEDT INFO: Initializing new Desktop session.
PyAEDT INFO: Log on console is enabled.
PyAEDT INFO: Log on file C:\Users\ansys\AppData\Local\Temp\pyaedt_ansys_03959212-e887-4503-b56f-1ed67b56ae17.log is enabled.
PyAEDT INFO: Log on AEDT is enabled.
PyAEDT INFO: Debug logger is disabled. PyAEDT methods will not be logged.
PyAEDT INFO: Launching PyAEDT with gRPC plugin.
PyAEDT INFO: New AEDT session is starting on gRPC port 55082
PyAEDT INFO: AEDT installation Path C:\Program Files\AnsysEM\v242\Win64
PyAEDT INFO: Ansoft.ElectronicsDesktop.2024.2 version started with process ID 2428.
PyAEDT INFO: EDB folder C:\Users\ansys\AppData\Local\Temp\pcb_NAYBNG.aedb has been imported to project pcb_NAYBNG
PyAEDT INFO: Active Design set to 0;Cell_3OZ25E
PyAEDT INFO: Aedt Objects correctly read
Plot geometry#
Plot the geometry. The EDB methods are also accessible from the Hfss3dlayout
class.
[23]:
h3d.modeler.edb.nets.plot(None)
PyAEDT INFO: Loading Modeler.
PyAEDT INFO: Modeler loaded.
PyAEDT INFO: EDB loaded.
PyAEDT INFO: Layers loaded.
PyAEDT INFO: Primitives loaded.
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Logger is initialized in EDB.
PyAEDT INFO: legacy v0.34.2
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: Database pcb_NAYBNG.aedb Opened in 2024.2
PyAEDT INFO: Cell Cell_3OZ25E Opened
PyAEDT INFO: Builder was initialized.
PyAEDT INFO: EDB initialized.
PyAEDT INFO: Plot Generation time 0.111
[23]:
(<Figure size 6000x3000 with 1 Axes>, <Axes: title={'center': 'Edb Top View'}>)
Create setup and sweeps#
Getters and setters facilitate the settings on the nested property dictionary. Previously, you had to use these commands:
setup.props["AdaptiveSettings"]["SingleFrequencyDataList"]["AdaptiveFrequencyData"]["AdaptiveFrequency"] = "20GHz"
setup.props["AdaptiveSettings"]["SingleFrequencyDataList"]["AdaptiveFrequencyData"]["MaxPasses"] = 4
You can now use the simpler approach that follows.
[24]:
setup = h3d.create_setup()
[25]:
setup["AdaptiveFrequency"] = "20GHz"
setup["AdaptiveSettings/SingleFrequencyDataList/AdaptiveFrequencyData/MaxPasses"] = 4
h3d.create_linear_count_sweep(
setupname=setup.name,
unit="GHz",
freqstart=20,
freqstop=50,
num_of_freq_points=1001,
sweepname="sweep1",
sweep_type="Interpolating",
interpolation_tol_percent=1,
interpolation_max_solutions=255,
save_fields=False,
use_q3d_for_dc=False,
)
PyAEDT WARNING: Argument `setupname` is deprecated for method `create_linear_count_sweep`; use `setup` instead.
PyAEDT WARNING: Argument `freqstart` is deprecated for method `create_linear_count_sweep`; use `start_frequency` instead.
PyAEDT WARNING: Argument `freqstop` is deprecated for method `create_linear_count_sweep`; use `stop_frequency` instead.
PyAEDT WARNING: Argument `sweepname` is deprecated for method `create_linear_count_sweep`; use `name` instead.
PyAEDT INFO: Linear count sweep sweep1 has been correctly created.
[25]:
<ansys.aedt.core.modules.solve_sweeps.SweepHFSS3DLayout at 0x21ec0e8f2e0>
Solve setup and create report#
Solve the project and create a report.
[26]:
h3d.analyze()
h3d.post.create_report(["db(S({0},{1}))".format(port_name, port_name)])
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/HFSS 3D Layout Design correctly changed.
PyAEDT INFO: Solving all design setups.
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/HFSS 3D Layout Design correctly changed.
PyAEDT INFO: Design setup None solved correctly in 0.0h 1.0m 27.0s
PyAEDT INFO: Parsing C:/Users/ansys/AppData/Local/Temp/pcb_NAYBNG.aedt.
PyAEDT INFO: File C:/Users/ansys/AppData/Local/Temp/pcb_NAYBNG.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.016706228256225586
PyAEDT INFO: PostProcessor class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
[26]:
<ansys.aedt.core.visualization.report.standard.Standard at 0x21ec8e158d0>
Plot results outside AEDT#
Plot results using Matplotlib.
[27]:
solution = h3d.post.get_solution_data(["S({0},{1})".format(port_name, port_name)])
solution.plot()
PyAEDT INFO: Solution Data Correctly Loaded.
[27]:
Close AEDT#
After the simulation completes, you can close AEDT or release it using the :func:dotnet.Desktop.release_desktop
method. All methods provide for saving the project before closing AEDT.
[28]:
h3d.save_project()
h3d.release_desktop()
PyAEDT INFO: Project pcb_NAYBNG Saved correctly
PyAEDT INFO: Desktop has been released and closed.
[28]:
True