Download this example

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


PCB DCIR analysis#

This example shows how to use PyAEDT to create a design in Q3D Extractor and run a DC IR drop simulation starting from an EDB project.

Keywords: Q3D, layout, DCIR.

Perform imports and define constants#

Perform required imports.

[1]:
import os
import tempfile
import time
[2]:
import ansys.aedt.core
import pyedb

Define constants.

[3]:
AEDT_VERSION = "2024.2"
NUM_CORES = 4
NG_MODE = False

Create temporary directory#

Create a temporary directory where downloaded data or dumped data can be stored. If you’d like to retrieve the project data for subsequent use, the temporary folder name is given by temp_folder.name.

[4]:
temp_folder = tempfile.TemporaryDirectory(suffix=".ansys")
# ## Set up project files and path
#
# Download needed project file and set up temporary project directory.
[5]:
aedb_project = ansys.aedt.core.downloads.download_file(
    "edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name
)
coil = ansys.aedt.core.downloads.download_file(
    source="inductance_3d_component",
    name="air_coil.a3dcomp",
    destination=temp_folder.name,
)
res = ansys.aedt.core.downloads.download_file(
    source="resistors", name="Res_0402.a3dcomp", destination=temp_folder.name
)
project_name = "HSD"
output_edb = os.path.join(temp_folder.name, project_name + ".aedb")
output_q3d = os.path.join(temp_folder.name, project_name + "_q3d.aedt")

Open EDB project#

Open the EDB project and create a cutout on the selected nets before exporting to Q3D.

[6]:
edb = pyedb.Edb(aedb_project, edbversion=AEDT_VERSION)
signal_nets = ["1.2V_AVDLL_PLL", "1.2V_AVDDL", "1.2V_DVDDL", "NetR106_1"]
ground_nets = ["GND"]
cutout_points = edb.cutout(
    signal_list=signal_nets,
    reference_list=ground_nets,
    output_aedb_path=output_edb,
)
PyAEDT INFO: Logger is initialized in EDB.
PyAEDT INFO: legacy v0.31.0
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 ANSYS-HSD_V1.aedb Opened in 2024.2
PyAEDT INFO: Cell main Opened
PyAEDT INFO: Builder was initialized.
PyAEDT INFO: EDB initialized.
PyAEDT INFO: EDB file save time: 0.00ms
PyAEDT INFO: Cutout Multithread started.
PyAEDT INFO: Net clean up Elapsed time: 0m 2sec
PyAEDT INFO: Extent Creation Elapsed time: 0m 0sec
PyAEDT INFO: 1988 Padstack Instances deleted. Elapsed time: 0m 1sec
PyAEDT INFO: 441 Primitives deleted. Elapsed time: 0m 2sec
PyAEDT INFO: 972 components deleted
PyAEDT INFO: EDB file save time: 0.00ms
PyAEDT INFO: Cutout completed. Elapsed time: 0m 5sec

Identify pin positions#

Identify [x,y] pin locations on the components to define where to assign sources and sinks for Q3D.

[7]:
pin_u11_scl = [
    i for i in edb.components["U11"].pins.values() if i.net_name == "1.2V_AVDLL_PLL"
]
pin_u9_1 = [i for i in edb.components["U9"].pins.values() if i.net_name == "1.2V_AVDDL"]
pin_u9_2 = [i for i in edb.components["U9"].pins.values() if i.net_name == "1.2V_DVDDL"]
pin_u11_r106 = [
    i for i in edb.components["U11"].pins.values() if i.net_name == "NetR106_1"
]

Append Z Positions#

Compute the Q3D 3D position. The units in EDB are meters so the factor 1000 converts from meters to millimeters.

[8]:
location_u11_scl = [i * 1000 for i in pin_u11_scl[0].position]
location_u11_scl.append(edb.components["U11"].upper_elevation * 1000)

location_u9_1_scl = [i * 1000 for i in pin_u9_1[0].position]
location_u9_1_scl.append(edb.components["U9"].upper_elevation * 1000)

location_u9_2_scl = [i * 1000 for i in pin_u9_2[0].position]
location_u9_2_scl.append(edb.components["U9"].upper_elevation * 1000)

location_u11_r106 = [i * 1000 for i in pin_u11_r106[0].position]
location_u11_r106.append(edb.components["U11"].upper_elevation * 1000)

Identify pin positions for 3D components#

Identify the pin positions where 3D components of passives are to be added.

[9]:
location_l2_1 = [i * 1000 for i in edb.components["L2"].pins["1"].position]
location_l2_1.append(edb.components["L2"].upper_elevation * 1000)
location_l4_1 = [i * 1000 for i in edb.components["L4"].pins["1"].position]
location_l4_1.append(edb.components["L4"].upper_elevation * 1000)

location_r106_1 = [i * 1000 for i in edb.components["R106"].pins["1"].position]
location_r106_1.append(edb.components["R106"].upper_elevation * 1000)

Save and close EDB#

Save and close EDB. Then, open EDT in HFSS 3D Layout to generate the 3D model.

[10]:
edb.save_edb()
edb.close_edb()
time.sleep(3)

h3d = ansys.aedt.core.Hfss3dLayout(
    output_edb, version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True
)
PyAEDT INFO: EDB file save time: 0.00ms
PyAEDT INFO: EDB file release time: 0.00ms
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_ddcc1e32-ba18-43e4-b101-99fe3fda490d.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 61696
PyAEDT INFO: AEDT installation Path C:\Program Files\AnsysEM\v242\Win64
PyAEDT INFO: Ansoft.ElectronicsDesktop.2024.2 version started with process ID 3212.
PyAEDT INFO: EDB folder C:\Users\ansys\AppData\Local\Temp\tmpisfd8nc9.ansys\HSD.aedb has been imported to project HSD
PyAEDT INFO: Active Design set to 0;main
PyAEDT INFO: Aedt Objects correctly read

Export to Q3D#

Create a dummy setup and export the layout to Q3D. The keep_net_name parameter reassigns Q3D net names from HFSS 3D Layout.

[11]:
setup = h3d.create_setup()
setup.export_to_q3d(output_q3d, keep_net_name=True)
h3d.close_project()
time.sleep(3)
PyAEDT INFO: Project: HSD, Design: main, [info]  Translation Info: Export complete (00:00:03). (04:37:28 AM  Nov 15, 2024)
PyAEDT INFO: Project: HSD, Design: main, [info]  File written to C:/Users/ansys/AppData/Local/Temp/tmpisfd8nc9.ansys/HSD_q3d.aedt. (04:37:28 AM  Nov 15, 2024)
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.31.0
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 HSD.aedb Opened in 2024.2
PyAEDT INFO: Cell main Opened
PyAEDT INFO: Builder was initialized.
PyAEDT INFO: EDB initialized.
PyAEDT INFO: Processing net NetR106_1...
PyAEDT INFO: Processing net GND...
PyAEDT INFO: Processing net 1.2V_DVDDL...
PyAEDT INFO: Processing net 1.2V_AVDLL_PLL...
PyAEDT INFO: Processing net 1.2V_AVDDL...
PyAEDT INFO: Net processing completed.
PyAEDT INFO: Processing vias...
PyAEDT INFO: Vias processing completed.
PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\tmpisfd8nc9.ansys\HSD_q3d.aedt.
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: Returning found Desktop session with PID 3212!
PyAEDT INFO: Project HSD_q3d has been opened.
PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\tmpisfd8nc9.ansys\HSD_q3d.aedt correctly loaded. Elapsed time: 0m 2sec
PyAEDT INFO: Active Design set to Q3DDesign1
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Materials class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Renaming primitives for net NetR106_1...
PyAEDT INFO: Renaming primitives for net GND...
PyAEDT INFO: Union of 51 objects has been executed.
PyAEDT INFO: Renaming primitives for net 1_2V_DVDDL...
PyAEDT INFO: Union of 12 objects has been executed.
PyAEDT INFO: Renaming primitives for net 1_2V_AVDLL_PLL...
PyAEDT INFO: Union of 5 objects has been executed.
PyAEDT INFO: Renaming primitives for net 1_2V_AVDDL...
PyAEDT INFO: Union of 5 objects has been executed.
PyAEDT INFO: 1 Nets have been identified: GND_1
PyAEDT INFO: Closing the AEDT Project HSD_q3d
PyAEDT INFO: Project HSD_q3d closed correctly
PyAEDT INFO: EDB file release time: 0.00ms
PyAEDT INFO: Closing the AEDT Project HSD
PyAEDT INFO: Project HSD closed correctly

Open Q3D#

Launch the newly created Q3D project.

[12]:
q3d = ansys.aedt.core.Q3d(output_q3d, version=AEDT_VERSION)
q3d.modeler.delete("GND")
q3d.modeler.delete("16_Bottom_L6")
q3d.delete_all_nets()
PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\tmpisfd8nc9.ansys\HSD_q3d.aedt.
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: Returning found Desktop session with PID 3212!
PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\tmpisfd8nc9.ansys\HSD_q3d.aedt correctly loaded. Elapsed time: 0m 0sec
PyAEDT INFO: Project HSD_q3d has been opened.
PyAEDT INFO: Active Design set to Q3DDesign1
PyAEDT INFO: Aedt Objects correctly read
PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Parsing design objects. This operation can take time
PyAEDT INFO: 3D Modeler objects parsed. Elapsed time: 0m 0sec
PyAEDT INFO: Deleted 1 Objects: GND.
PyAEDT INFO: Deleted 1 Objects: 16_Bottom_L6.
[12]:
True

Insert inductors#

Create coordinate systems and place 3D component inductors.

[13]:
q3d.modeler.create_coordinate_system(location_l2_1, name="L2")
comp = q3d.modeler.insert_3d_component(coil, coordinate_system="L2")
comp.rotate(q3d.AXIS.Z, -90)
comp.parameters["n_turns"] = "3"
comp.parameters["d_wire"] = "100um"
q3d.modeler.set_working_coordinate_system("Global")
q3d.modeler.create_coordinate_system(location_l4_1, name="L4")
comp2 = q3d.modeler.insert_3d_component(coil, coordinate_system="L4")
comp2.rotate(q3d.AXIS.Z, -90)
comp2.parameters["n_turns"] = "3"
comp2.parameters["d_wire"] = "100um"
q3d.modeler.set_working_coordinate_system("Global")

q3d.modeler.set_working_coordinate_system("Global")
q3d.modeler.create_coordinate_system(location_r106_1, name="R106")
comp3 = q3d.modeler.insert_3d_component(
    res, geometry_parameters={"$Resistance": 2000}, coordinate_system="R106"
)
comp3.rotate(q3d.AXIS.Z, -90)

q3d.modeler.set_working_coordinate_system("Global")
[13]:
True

Delete dielectrics#

Delete all dielectric objects since they are not needed in DC analysis.

[14]:
q3d.modeler.delete(q3d.modeler.get_objects_by_material("Megtron4"))
q3d.modeler.delete(q3d.modeler.get_objects_by_material("Megtron4_2"))
q3d.modeler.delete(q3d.modeler.get_objects_by_material("Megtron4_3"))
q3d.modeler.delete(q3d.modeler.get_objects_by_material("Solder Resist"))

objs_copper = q3d.modeler.get_objects_by_material("copper")
objs_copper_names = [i.name for i in objs_copper]
q3d.plot(
    show=False,
    assignment=objs_copper_names,
    plot_as_separate_objects=False,
    output_file=os.path.join(temp_folder.name, "Q3D.jpg"),
    plot_air_objects=False,
)
PyAEDT INFO: Deleted 4 Objects: DE1,DE3,DE5,DE7.
PyAEDT INFO: Deleted 6 Objects: DE2,DE6,Inner1_GND1_t_fill,Inner2_PWR1_t_fill,Inner5_PWR2_t_fill,Inner6_GND2_t_fill.
PyAEDT INFO: Deleted 3 Objects: Inner3_Sig1_t_fill,Inner4_Sig2_t_fill,Megtron4_1mm.
PyAEDT INFO: Deleted 2 Objects: 16_Bottom_t_fill,1_Top_t_fill.
PyAEDT INFO: PostProcessor class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec
C:\actions-runner\_work\pyaedt-examples\pyaedt-examples\.venv\lib\site-packages\pyvista\jupyter\notebook.py:37: UserWarning: Failed to use notebook backend:

No module named 'trame'

Falling back to a static output.
  warnings.warn(
../../../../_images/examples_high_frequency_layout_power_integrity_dcir_q3d_26_2.png
[14]:
<ansys.aedt.core.visualization.plot.pyvista.ModelPlotter at 0x26233fad900>

Assign source and sink#

Use previously calculated positions to identify faces. Select the net 1_Top and assign sources and sinks on nets.

[15]:
sink_f = q3d.modeler.create_circle(q3d.PLANE.XY, location_u11_scl, 0.1)
source_f1 = q3d.modeler.create_circle(q3d.PLANE.XY, location_u9_1_scl, 0.1)
source_f2 = q3d.modeler.create_circle(q3d.PLANE.XY, location_u9_2_scl, 0.1)
source_f3 = q3d.modeler.create_circle(q3d.PLANE.XY, location_u11_r106, 0.1)
sources_objs = [source_f1, source_f2, source_f3]

q3d.auto_identify_nets()

identified_net = q3d.nets[0]

q3d.sink(sink_f, net_name=identified_net)

source1 = q3d.source(source_f1, net_name=identified_net)

source2 = q3d.source(source_f2, net_name=identified_net)
source3 = q3d.source(source_f3, net_name=identified_net)
sources_bounds = [source1, source2, source3]

q3d.edit_sources(
    dcrl={
        "{}:{}".format(source1.props["Net"], source1.name): "-1.0A",
        "{}:{}".format(source2.props["Net"], source2.name): "-1.0A",
        "{}:{}".format(source2.props["Net"], source3.name): "-1.0A",
    }
)
PyAEDT INFO: Materials class has been initialized! Elapsed time: 0m 0sec
PyAEDT INFO: 1 Nets have been identified: 1_2V_AVDDL
[15]:
True

Create setup#

Create a setup and a frequency sweep from DC to 2GHz. Then, analyze the project.

[16]:
setup = q3d.create_setup()
setup.dc_enabled = True
setup.capacitance_enabled = False
setup.ac_rl_enabled = False
setup.props["SaveFields"] = True
setup.props["DC"]["Cond"]["MaxPass"] = 3
setup.analyze()
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed.
PyAEDT INFO: Solving design setup MySetupAuto
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed.
PyAEDT INFO: Design setup MySetupAuto solved correctly in 0.0h 3.0m 23.0s

Solve setup#

[17]:
q3d.save_project()
q3d.analyze_setup(setup.name, cores=NUM_CORES)
PyAEDT INFO: Project HSD_q3d Saved correctly
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed.
PyAEDT INFO: Solving design setup MySetupAuto
PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed.
PyAEDT INFO: Design setup MySetupAuto solved correctly in 0.0h 0.0m 47.0s
[17]:
True

Create a named expression#

Use PyAEDT advanced fields calculator to add from the expressions catalog the voltage drop.

[18]:
voltage_drop = q3d.post.fields_calculator.add_expression("voltage_drop", None)

Create Phi plot#

Compute ACL solutions and plot them.

[19]:
plot1 = q3d.post.create_fieldplot_surface(
    q3d.modeler.get_objects_by_material("copper"),
    quantity=voltage_drop,
    intrinsics={"Freq": "1GHz"},
)

q3d.post.plot_field_from_fieldplot(
    plot1.name,
    project_path=temp_folder.name,
    mesh_plot=False,
    image_format="jpg",
    view="isometric",
    show=False,
    plot_cad_objs=False,
    log_scale=False,
)
C:\actions-runner\_work\pyaedt-examples\pyaedt-examples\.venv\lib\site-packages\pyvista\jupyter\notebook.py:37: UserWarning: Failed to use notebook backend:

No module named 'trame'

Falling back to a static output.
  warnings.warn(
../../../../_images/examples_high_frequency_layout_power_integrity_dcir_q3d_36_1.png
[19]:
<ansys.aedt.core.visualization.plot.pyvista.ModelPlotter at 0x26234226080>

Compute voltage on source circles#

Use PyAEDT advanced field calculator to compute the voltage on source circles and get the value using the get_solution_data() method.

[20]:
v_surface = {
    "name": "",
    "description": "Maximum value of voltage on a surface",
    "design_type": ["Q3D Extractor"],
    "fields_type": ["DC R/L Fields"],
    "primary_sweep": "Freq",
    "assignment": "",
    "assignment_type": ["Face", "Sheet"],
    "operations": [
        f"NameOfExpression({voltage_drop})",
        "EnterSurface('assignment')",
        "Operation('SurfaceValue')",
        "Operation('Maximum')",
    ],
    "report": ["Field_3D"],
}
for source_circle, source_bound in zip(sources_objs, sources_bounds):
    v_surface["name"] = "V{}".format(source_bound.name)
    q3d.post.fields_calculator.add_expression(v_surface, source_circle.name)

    data = q3d.post.get_solution_data(
        "V{}".format(source_bound.name),
        q3d.nominal_adaptive,
        variations={"Freq": "1GHz"},
        report_category="DC R/L Fields",
    )
    if data:
        print(data.data_real("V{}".format(source_bound.name)))
PyAEDT INFO: Solution Data Correctly Loaded.
[3.2604179368978787]
PyAEDT INFO: Solution Data Correctly Loaded.
[3.2604179368978787]
PyAEDT INFO: Solution Data Correctly Loaded.
[3.2604179368978787]

Release AEDT#

[21]:
q3d.save_project()
q3d.release_desktop()
# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.
time.sleep(3)
PyAEDT INFO: Project HSD_q3d 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.

[22]:
temp_folder.cleanup()

Download this example

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