Download this example
Download this example as a Jupyter Notebook or as a Python script.
Coplanar waveguide setup and analysis#
This example shows how to create a CPWG (coplanar waveguide with ground) design in 2D Extractor and calculate the impedance.
Keywords: Q2D, CPWG, Coplanar Waveguide.
Prerequisites#
Perform imports#
[1]:
import os
import tempfile
import time
import ansys.aedt.core
Define constants#
Constants help ensure consistency and avoid repetition throughout the example.
[2]:
AEDT_VERSION = "2025.1"
NUM_CORES = 4
NG_MODE = False # Run the example without opening the UI.
Create temporary directory#
Create a temporary working directory. The name of the working folder is stored in temp_folder.name
.
Note: The final cell in the notebook cleans up the temporary folder. If you want to retrieve the AEDT project and data, do so before executing the final cell in the notebook.
[3]:
temp_folder = tempfile.TemporaryDirectory(suffix=".ansys")
Launch AEDT#
Launch an instance of the Ansys Electronics Desktop (AEDT) in graphical mode. The Q2d
class inserts a 2-D Extractor design in AEDT.
[4]:
q2d = ansys.aedt.core.Q2d(
version=AEDT_VERSION,
non_graphical=NG_MODE,
new_desktop=True,
project=os.path.join(temp_folder.name, "cpwg"),
design="coplanar_waveguide",
)
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.16.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_cf3f91b4-84e2-4618-b47b-9d2cdf0ecf4b.log is enabled.
PyAEDT INFO: Log on AEDT is disabled.
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 50517.
PyAEDT INFO: Electronics Desktop started on gRPC port: 50517 after 6.993967771530151 seconds.
PyAEDT INFO: AEDT installation Path C:\Program Files\ANSYS Inc\v251\AnsysEM
PyAEDT INFO: Ansoft.ElectronicsDesktop.2025.1 version started with process ID 11152.
PyAEDT INFO: Project cpwg has been created.
PyAEDT INFO: Added design 'coplanar_waveguide' of type 2D Extractor.
PyAEDT INFO: Aedt Objects correctly read
Model Preparation#
Define parameters#
Define independent parameters and create expressions that will be used to create the coplanar waveguide cross-section.
The dict cpw_params
contains the independent parameters used to create the model.
[5]:
cpw_params = {
"sig_bot_w": "150um", # Signal conductor width - bottom.
"e_factor": "2", # Etch factor for trapezoidal cross-section.
"gnd_w": "500um", # Width of the ground conductor.
"clearance": "150um",
"cond_h": "50um", # Conductor height
"d_h": "150um",
"sm_h": "20um", # Solder mask height
}
Create expressions#
Expressions are passed to the methods that we’ll use to create the geometric cross-section of the coplanar waveguide. These expressions depend on the independent parameters defined in the previous cell, cpw_params
.
[6]:
delta_w_half = "cond_h/e_factor"
sig_top_w = f"(sig_bot_w - 2 * {delta_w_half})" # Signal top conductor width
co_gnd_top_w = f"(gnd_w - 2 * {delta_w_half})" # Width of top ground conductor
model_w = "2 * gnd_w + 2 * clearance + sig_bot_w" # Total width of the model.
The following expressions define layer thicknesses parameterization.
[7]:
layer_1_lh = 0
layer_1_uh = "cond_h"
layer_2_lh = layer_1_uh + " + d_h"
layer_2_uh = layer_2_lh + " + cond_h"
Assign independent parameters in the Q2d design.
[8]:
for name, value in cpw_params.items():
q2d[name] = value
Create the signal conductor by drawing two lines and connecting them to create a 2D sheet.
[9]:
base_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_lh, 0], ["sig_bot_w", layer_2_lh, 0]], name="signal"
)
top_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_uh, 0], [sig_top_w, layer_2_uh, 0]]
)
q2d.modeler.move(assignment=[top_line_obj], vector=[delta_w_half, 0, 0])
q2d.modeler.connect([base_line_obj, top_line_obj])
q2d.modeler.move(
assignment=[base_line_obj], vector=["gnd_w+clearance", 0, 0]
)
PyAEDT INFO: Modeler2D class has been initialized!
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 2sec
PyAEDT INFO: Materials class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Connection Correctly created
[9]:
True
Create adjacent ground layers.
[10]:
base_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_lh, 0], ["gnd_w", layer_2_lh, 0]], name="co_gnd_left"
)
top_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_uh, 0], [co_gnd_top_w, layer_2_uh, 0]]
)
q2d.modeler.move(assignment=[top_line_obj], vector=[delta_w_half, 0, 0])
q2d.modeler.connect([base_line_obj, top_line_obj])
base_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_lh, 0], ["gnd_w", layer_2_lh, 0]], name="co_gnd_right"
)
top_line_obj = q2d.modeler.create_polyline(
points=[[0, layer_2_uh, 0], [co_gnd_top_w, layer_2_uh, 0]]
)
q2d.modeler.move(assignment=[top_line_obj], vector=[delta_w_half, 0, 0])
q2d.modeler.connect([base_line_obj, top_line_obj])
q2d.modeler.move(
assignment=[base_line_obj],
vector=[f"gnd_w+clearance*2+sig_bot_w", 0, 0],
)
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Connection Correctly created
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Connection Correctly created
[10]:
True
Create a reference ground plane.
[11]:
q2d.modeler.create_rectangle(
origin=[0, layer_1_lh, 0], sizes=[model_w, "cond_h"], name="ref_gnd"
)
[11]:
<ansys.aedt.core.modeler.cad.object_3d.Object3d at 0x25986796950>
Define the substrate.
[12]:
q2d.modeler.create_rectangle(
origin=[0, layer_1_uh, 0],
sizes=[model_w, "d_h"],
name="Dielectric",
material="FR4_epoxy",
)
[12]:
<ansys.aedt.core.modeler.cad.object_3d.Object3d at 0x25988699e40>
Assign a conformal coating.
[13]:
sm_obj_list = []
ids = [0, 1, 2]
if AEDT_VERSION < "2023.1": # Support old versions of AEDT.
ids = [1, 2, 3]
for obj_name in ["signal", "co_gnd_left", "co_gnd_right"]:
obj = q2d.modeler.get_object_from_name(obj_name)
e_obj_list = []
for i in ids:
e_obj = q2d.modeler.create_object_from_edge(obj.edges[i])
e_obj_list.append(e_obj)
e_obj_1 = e_obj_list[0]
q2d.modeler.unite(e_obj_list)
_ = q2d.modeler.sweep_along_vector(
assignment=e_obj_1.id, sweep_vector=[0, "sm_h", 0]
)
sm_obj_list.append(e_obj_1)
new_obj = q2d.modeler.create_rectangle(
origin=["gnd_w", layer_2_lh, 0], sizes=["clearance", "sm_h"]
)
sm_obj_list.append(new_obj)
new_obj2 = q2d.modeler.create_rectangle(
origin=["gnd_w", layer_2_lh, 0], sizes=["clearance", "sm_h"]
)
q2d.modeler.move(assignment=[new_obj2], vector=["sig_bot_w+clearance", 0, 0])
sm_obj_list.append(new_obj2)
sm_obj = sm_obj_list[0]
q2d.modeler.unite(sm_obj_list)
sm_obj.material_name = "SolderMask"
sm_obj.color = (0, 150, 100)
sm_obj.name = "solder_mask"
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Union of 3 objects has been executed.
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Union of 3 objects has been executed.
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Union of 3 objects has been executed.
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: Refreshing bodies from Object Info
PyAEDT INFO: Bodies Info Refreshed Elapsed time: 0m 0sec
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Union of 5 objects has been executed.
Assign sources and boundary conditions#
Define the signal conductor.
[14]:
signal_conductor = q2d.modeler.get_object_from_name("signal")
q2d.assign_single_conductor(
name=signal_conductor.name,
assignment=[signal_conductor],
conductor_type="SignalLine",
solve_option="SolveOnBoundary",
units="mm",
)
PyAEDT INFO: Boundary SignalLine signal has been created.
[14]:
<ansys.aedt.core.modules.boundary.common.BoundaryObject at 0x2599e88da20>
Assign the Huray model for conductive losses on the signal trace.
[15]:
q2d.assign_huray_finitecond_to_edges(signal_conductor.edges,
radius="0.5um",
ratio=3,
name="b_" + signal_conductor.name
)
PyAEDT INFO: Boundary Finite Conductivity b_signal has been created.
[15]:
<ansys.aedt.core.modules.boundary.common.BoundaryObject at 0x2599e88c040>
Define the return path.
[16]:
reference_conductors = [q2d.modeler.get_object_from_name(i)
for i in ["co_gnd_left", "co_gnd_right", "ref_gnd"]
]
q2d.assign_single_conductor(
name="gnd",
assignment=reference_conductors,
conductor_type="ReferenceGround",
solve_option="SolveOnBoundary",
units="mm",
)
PyAEDT INFO: Boundary ReferenceGround gnd has been created.
[16]:
<ansys.aedt.core.modules.boundary.common.BoundaryObject at 0x2599e88dcf0>
Define solution setup#
The solution steup specifies the frequency range for the solution and other solution settings
[17]:
setup = q2d.create_setup(setupname="new_setup")
sweep = setup.add_sweep(name="sweep1")
sweep.props["RangeType"] = "LinearStep"
sweep.props["RangeStart"] = "1GHz"
sweep.props["RangeStep"] = "100MHz"
sweep.props["RangeEnd"] = "5GHz"
sweep.props["SaveFields"] = False
sweep.props["SaveRadFields"] = False
sweep.props["Type"] = "Interpolating"
sweep.update()
PyAEDT INFO: Key setupname matched internal key 'Name' with confidence of 46.
PyAEDT INFO: Parsing C:/Users/ansys/AppData/Local/Temp/tmpda0s8_pp.ansys/cpwg.aedt.
PyAEDT INFO: File C:/Users/ansys/AppData/Local/Temp/tmpda0s8_pp.ansys/cpwg.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: aedt file load time 0.015624284744262695
[17]:
True
Run analysis#
[18]:
q2d.analyze(cores=NUM_CORES)
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/2D Extractor correctly changed.
PyAEDT INFO: Solving all design setups.
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/2D Extractor correctly changed.
PyAEDT INFO: Design setup None solved correctly in 0.0h 0.0m 18.0s
[18]:
True
Postprocess#
View the impedance over frequency.
[19]:
data = q2d.post.get_solution_data(expressions="Z0(signal,signal)", context="Original")
data.plot()
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 Data Correctly Loaded.
[19]:


Finish#
Save the project#
[20]:
q2d.save_project()
q2d.release_desktop()
# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.
time.sleep(3)
PyAEDT INFO: Project cpwg Saved correctly
PyAEDT INFO: Desktop has been released and closed.
Clean up#
All project files are saved in the folder temp_folder.name
. If you’ve run this example as a Jupyter notebook, you can retrieve those project files. The following cell removes all temporary files, including the project folder.
[21]:
temp_folder.cleanup()
Download this example
Download this example as a Jupyter Notebook or as a Python script.