diff --git a/.gitignore b/.gitignore index 66951207d..6567db02e 100644 --- a/.gitignore +++ b/.gitignore @@ -106,7 +106,7 @@ test_results-*.xml .env .venv env/ -venv/ +venv*/ /venv/ ENV/ env.bak/ diff --git a/doc/source/conf.py b/doc/source/conf.py index e445672ba..ff5c29d02 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -26,6 +26,9 @@ from sphinx.builders.latex import LaTeXBuilder from sphinx.util import logging +os.environ["PYAEDT_NON_GRAPHICAL"] = "1" +os.environ["PYAEDT_DOC_GENERATION"] = "1" + LaTeXBuilder.supported_image_types = ["image/png", "image/pdf", "image/svg+xml"] logger = logging.getLogger(__name__) @@ -260,6 +263,7 @@ def convert_examples_into_notebooks(app): "interference_type.py", "interference.py", "hfss_emit.py", + "component_conversion.py", ) # NOTE: Only convert the examples if the workflow isn't tagged as coupling HTML and PDF build. diff --git a/examples/00_edb/legacy_standalone/GDS_workflow.py b/examples/00_edb/legacy_standalone/GDS_workflow.py new file mode 100644 index 000000000..5ba2c5f90 --- /dev/null +++ b/examples/00_edb/legacy_standalone/GDS_workflow.py @@ -0,0 +1,118 @@ +# # EDB: Edit Control File and import gds +# +# This example demonstrates how to import a gds layout for subsequent +# simulation with HFSS. + +# Perform imports. + +# + +import os +import shutil +import tempfile + +import pyedb +from pyedb.dotnet.edb_core.edb_data.control_file import ControlFile +from pyedb.misc.downloads import download_file + +# - + +# ## Fetch Example Data +# +# Download the EDB folder and copy it to a temporary folder. +# The following files are used in this example: +# +# - _sky130_fictious_dtc_exmple_contol_no_map.xml_ +# defines physical information such +# as material properties, stackup layers, and boundary conditions. +# - _dummy_layermap.map_ +# maps properties to stackup layers. + +# + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +control_fn = "sky130_fictitious_dtc_example_control_no_map.xml" +gds_fn = "sky130_fictitious_dtc_example.gds" +layer_map = "dummy_layermap.map" + +local_path = download_file("gds", destination=temp_dir.name) +c_file_in = os.path.join(local_path, control_fn) +c_map = os.path.join(local_path, layer_map) +gds_in = os.path.join(local_path, gds_fn) +gds_out = os.path.join(temp_dir.name, "gds_out.gds") +shutil.copy2(gds_in, gds_out) +# - + +# ## Control file +# +# A Control file is an xml file which purpose if to provide additional information during +# import phase. It can include, materials, stackup, setup, boundaries and settings. +# In this example we will import an existing xml, integrate it with a layer mapping file of gds +# and then adding setup and boundaries. + +c = ControlFile(c_file_in, layer_map=c_map) + +# ## Set up simulation +# +# This code sets up a simulation with HFSS and adds a frequency sweep. + +setup = c.setups.add_setup("Setup1", "1GHz", 0.02, 10) +setup.add_sweep("Sweep1", "0.01GHz", "5GHz", "0.1GHz") + +# ## Provide additional stackup settings +# +# After import, you can change the stackup settings and add or remove layers or materials. + +c.stackup.units = "um" +c.stackup.dielectrics_base_elevation = -100 +c.stackup.metal_layer_snapping_tolerance = "10nm" +for via in c.stackup.vias: + via.create_via_group = True + via.snap_via_group = True + +# ## Define boundary settings +# +# Boundaries can include ports, components and boundary extent. + +c.boundaries.units = "um" +c.boundaries.add_port("P1", x1=223.7, y1=222.6, layer1="Metal6", x2=223.7, y2=100, layer2="Metal6") +c.boundaries.add_extent() +comp = c.components.add_component("B1", "BGA", "IC", "Flip chip", "Cylinder") +comp.solder_diameter = "65um" +comp.add_pin("1", "81.28", "84.6", "met2") +comp.add_pin("2", "211.28", "84.6", "met2") +comp.add_pin("3", "211.28", "214.6", "met2") +comp.add_pin("4", "81.28", "214.6", "met2") +c.import_options.import_dummy_nets = True + +# ## Write XML file +# +# After all settings are ready, you can write an XML file. + +c.write_xml(os.path.join(temp_dir.name, "output.xml")) + +# ## Open EDB +# +# Import the gds and open the edb. + +# + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(gds_out, edbversion=edb_version, technology_file=os.path.join(temp_dir.name, "output.xml")) +# - + +# ## Plot stackup +# +# Plot the stackup. + +edb.stackup.plot(first_layer="met1") + +# ## Close EDB +# +# Close the project. + +edb.close_edb() + +# Clean up the temporary folder. + +temp_dir.cleanup() diff --git a/examples/00_edb/legacy_standalone/Plot_nets.py b/examples/00_edb/legacy_standalone/Plot_nets.py new file mode 100644 index 000000000..24f12b0de --- /dev/null +++ b/examples/00_edb/legacy_standalone/Plot_nets.py @@ -0,0 +1,81 @@ +# # EDB: plot nets with Matplotlib +# +# This example shows how to use the ``Edb`` class to view nets, layers and +# via geometry directly in Python. The methods demonstrated in this example +# rely on +# [matplotlib](https://matplotlib.org/cheatsheets/_images/cheatsheets-1.png). + +# ## Perform required imports +# +# Perform required imports, which includes importing a section. + +# + +import tempfile + +import pyedb +from pyedb.misc.downloads import download_file + +# - + +# ## Download the EDB and copy it into the temporary folder. + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +targetfolder = download_file("edb/ANSYS-HSD_V1.aedb", destination=temp_dir.name) + +# ## Create an instance of the Electronics Database using the `pyedb.Edb` class. +# +# > Note that units are SI. + +# + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(edbpath=targetfolder, edbversion=edb_version) +# - + +# Display the nets on a layer. You can display the net geometry directly in Python using +# ``matplotlib`` from the ``pyedb.Edb`` class. + +edb.nets.plot("AVCC_1V3") + +# You can view multiple nets by passing a list containing the net +# names to the ``plot()`` method. + +edb.nets.plot(["GND", "GND_DP", "AVCC_1V3"], color_by_net=True) + +# You can display all copper on a single layer by passing ``None`` +# as the first argument. The second argument is a list +# of layers to plot. In this case, only one +# layer is to be displayed. + +edb.nets.plot(None, ["1_Top"], color_by_net=True, plot_components_on_top=True) + +# Display a side view of the layers and padstack geometry using the +# ``Edb.stackup.plot()`` method. + +edb.stackup.plot(scale_elevation=False, plot_definitions=["c100hn140", "c35"]) + +# ## Creating coaxial port on component U1 and all ddr4_dqs nets +# Selecting all nets from ddr4_dqs and component U1 and create coaxial ports +# On corresponding pins. + +comp_u1 = edb.components.instances["U1"] +signal_nets = [net for net in comp_u1.nets if "ddr4_dqs" in net.lower()] +edb.hfss.create_coax_port_on_component("U1", net_list=signal_nets) +edb.components.set_solder_ball(component="U1", sball_diam="0.3mm", sball_height="0.3mm") + +# ## Renaming all ports +# Renaming all port with _renamed string as suffix example. + +for port_name, port in edb.ports.items(): + port.name = f"{port_name}_renamed" + + +# Close the EDB. + +edb.close_edb() + +# Remove all files and the temporary directory. + +temp_dir.cleanup() diff --git a/examples/00_edb/legacy_standalone/_static/cpwg.png b/examples/00_edb/legacy_standalone/_static/cpwg.png new file mode 100644 index 000000000..7dd9227f5 Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/cpwg.png differ diff --git a/examples/00_edb/legacy_standalone/_static/diff_via.png b/examples/00_edb/legacy_standalone/_static/diff_via.png new file mode 100644 index 000000000..d444d1ccb Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/diff_via.png differ diff --git a/examples/00_edb/legacy_standalone/_static/gds.png b/examples/00_edb/legacy_standalone/_static/gds.png new file mode 100644 index 000000000..e633e23a9 Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/gds.png differ diff --git a/examples/00_edb/legacy_standalone/_static/ipc.png b/examples/00_edb/legacy_standalone/_static/ipc.png new file mode 100644 index 000000000..b55811c45 Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/ipc.png differ diff --git a/examples/00_edb/legacy_standalone/_static/plot_nets.png b/examples/00_edb/legacy_standalone/_static/plot_nets.png new file mode 100644 index 000000000..ee410a98e Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/plot_nets.png differ diff --git a/examples/00_edb/legacy_standalone/_static/siwave_dcir.png b/examples/00_edb/legacy_standalone/_static/siwave_dcir.png new file mode 100644 index 000000000..63ba977f8 Binary files /dev/null and b/examples/00_edb/legacy_standalone/_static/siwave_dcir.png differ diff --git a/examples/00_edb/legacy_standalone/differential_vias.py b/examples/00_edb/legacy_standalone/differential_vias.py new file mode 100644 index 000000000..0d1c63d11 --- /dev/null +++ b/examples/00_edb/legacy_standalone/differential_vias.py @@ -0,0 +1,83 @@ +# # EDB: geometry creation + +# This example shows how you can use EDB to create a layout. +# ## Final expected project +# +# +# +# ## Import EDB layout object +# Import the EDB layout object and initialize it on version 2023 R2. + +# + +import os +import tempfile + +import pyedb + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +aedb_path = os.path.join(temp_dir.name, "create_via.aedb") +print(f"AEDB file path: {aedb_path}") + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(edbpath=aedb_path, edbversion=edb_version) +# - + +# ## Add stackup layers +# Add stackup layers. +# A stackup can be created layer by layer or imported from a CSV file or XML file. + +edb.stackup.add_layer("GND") +edb.stackup.add_layer("Diel", "GND", layer_type="dielectric", thickness="0.1mm", material="FR4_epoxy") +edb.stackup.add_layer("TOP", "Diel", thickness="0.05mm") + +# ## Create signal net and ground planes +# Create a signal net and ground planes. + +points = [[0.0, 0], [100e-3, 0.0]] +edb.modeler.create_trace(points, "TOP", width=1e-3) +points = [[0.0, 1e-3], [0.0, 10e-3], [100e-3, 10e-3], [100e-3, 1e-3], [0.0, 1e-3]] +edb.modeler.create_polygon(points, "TOP") +points = [[0.0, -1e-3], [0.0, -10e-3], [100e-3, -10e-3], [100e-3, -1e-3], [0.0, -1e-3]] +edb.modeler.create_polygon(points, "TOP") + + +# ## Create vias with parametric positions +# Create vias with parametric positions. + +edb.padstacks.create("MyVia") +edb.padstacks.place([5e-3, 5e-3], "MyVia") +edb.padstacks.place([15e-3, 5e-3], "MyVia") +edb.padstacks.place([35e-3, 5e-3], "MyVia") +edb.padstacks.place([45e-3, 5e-3], "MyVia") +edb.padstacks.place([5e-3, -5e-3], "MyVia") +edb.padstacks.place([15e-3, -5e-3], "MyVia") +edb.padstacks.place([35e-3, -5e-3], "MyVia") +edb.padstacks.place([45e-3, -5e-3], "MyVia") + + +# ## Generate geometry plot + +edb.nets.plot(None, color_by_net=True) + +# ## Generate stackup plot + +edb.stackup.plot(plot_definitions="MyVia") + +# ## Save and close EDB +# Save and close EDB. + +if edb: + edb.save_edb() + edb.close_edb() +print("EDB saved correctly to {}. You can import in AEDT.".format(aedb_path)) + +# ### Clean up temporary directory +# +# The following command removes the project and the temporary directory. +# If you'd like to save this project, save it to a folder of your choice +# prior to running the following cell. + +temp_dir.cleanup() diff --git a/examples/00_edb/legacy_standalone/edb_to_ipc2581.py b/examples/00_edb/legacy_standalone/edb_to_ipc2581.py new file mode 100644 index 000000000..a28a91045 --- /dev/null +++ b/examples/00_edb/legacy_standalone/edb_to_ipc2581.py @@ -0,0 +1,72 @@ +# # EDB: IPC2581 export +# +# This example shows how you can use PyAEDT to export an IPC2581 file. +# +# Perform required imports, which includes importing a section. + +# + +import os +import tempfile + +import pyedb +from pyedb.generic.general_methods import generate_unique_name +from pyedb.misc.downloads import download_file + +# - + +# ## Download the AEDB file and copy it in the temporary folder. + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +targetfile = download_file("edb/ANSYS-HSD_V1.aedb", destination=temp_dir.name) +ipc2581_file_name = os.path.join(temp_dir.name, "Ansys_Hsd.xml") +print(targetfile) + +# ## Launch EDB +# +# Launch the `pyedb.Edb` class, using EDB 2023. +# > Note that length dimensions passed to EDB are in SI units. + +# + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(edbpath=targetfile, edbversion=edb_version) +# - + +# ## Parametrize the width of a trace. + +edb.modeler.parametrize_trace_width("A0_N", parameter_name=generate_unique_name("Par"), variable_value="0.4321mm") + +# ## Create a cutout and plot it. + +signal_list = [] +for net in edb.nets.netlist: + if "PCIe" in net: + signal_list.append(net) +power_list = ["GND"] +edb.cutout( + signal_list=signal_list, + reference_list=power_list, + extent_type="ConvexHull", + expansion_size=0.002, + use_round_corner=False, + number_of_threads=4, + remove_single_pin_components=True, + use_pyaedt_extent_computing=True, + extent_defeature=0, +) +edb.nets.plot(None, None, color_by_net=True) + +# ## Export the EDB to an IPC2581 file. + +edb.export_to_ipc2581(ipc2581_file_name, "inch") +print("IPC2581 File has been saved to {}".format(ipc2581_file_name)) + +# ## Close EDB + +edb.close_edb() + +# ## Clean up the temporary directory + +temp_dir.cleanup() diff --git a/examples/00_edb/legacy_standalone/siwave_dcir.py b/examples/00_edb/legacy_standalone/siwave_dcir.py new file mode 100644 index 000000000..82a5d74d7 --- /dev/null +++ b/examples/00_edb/legacy_standalone/siwave_dcir.py @@ -0,0 +1,209 @@ +# # EDB: SIwave DC-IR Analysis +# +# This example demonstrates the use of EDB to interact with a PCB +# layout and run DC-IR analysis in SIwave. +# Perform required imports + +# + +import os +import tempfile +import time + +import pyedb +from pyedb.misc.downloads import download_file + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +targetfile = download_file("edb/ANSYS-HSD_V1.aedb", destination=temp_dir.name) + +siwave_file = os.path.join(os.path.dirname(targetfile), "ANSYS-HSD_V1.siw") +print(targetfile) +aedt_file = targetfile[:-4] + "aedt" +# - + +# ## Launch Ansys Electronics Database (EDB) +# +# Instantiate an instance of the `pyedb.Edb` class using SI units. + +# + +if os.path.exists(aedt_file): + os.remove(aedt_file) + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(edbpath=targetfile, edbversion=edb_version) +# - + +# ## Identify nets and components +# +# The ``Edb.nets.netlist`` and ``Edb.components.instances`` properties contain information +# about all of the nets and components. The following cell uses this information to print the +# number of nets and components. + +print("Nets {}".format(len(edb.nets.netlist))) +start = time.time() +print("Components {}".format(len(edb.components.instances.keys()))) +print("elapsed time = ", time.time() - start) + +# ## Identify pin positions +# +# This code shows how to obtain all pins for a specific component and +# print the ``[x, y]`` position of each pin. + +pins = edb.components["U2"].pins +count = 0 +for pin in edb.components["U2"].pins.values(): + if count < 10: # Only print the first 10 pin coordinates. + print(pin.position) + elif count == 10: + print("...and many more.") + else: + pass + count += 1 + +# Get all nets connected to a specific component. Print +# the pin and the name of the net that it is connected to. + +connections = edb.components.get_component_net_connection_info("U2") +n_print = 0 # Counter to limit the number of printed lines. +print_max = 15 +for m in range(len(connections["pin_name"])): + ref_des = connections["refdes"][m] + pin_name = connections["pin_name"][m] + net_name = connections["net_name"][m] + if net_name != "" and (n_print < print_max): + print('{}, pin {} -> net "{}"'.format(ref_des, pin_name, net_name)) + n_print += 1 + elif n_print == print_max: + print("...and many more.") + n_print += 1 + +# Compute rats. + +rats = edb.components.get_rats() + +# ## Identify connected nets +# +# The ``get_dcconnected_net_list()`` method retrieves a list of +# all DC-connected power nets. Each group of connected nets is returned +# as a [set](https://docs.python.org/3/tutorial/datastructures.html#sets). +# The first argument to the method is the list of ground nets, which are +# not considered in the search for connected nets. + +GROUND_NETS = ["GND", "GND_DP"] +dc_connected_net_list = edb.nets.get_dcconnected_net_list(GROUND_NETS) +for pnets in dc_connected_net_list: + print(pnets) + +# ## Power Tree +# +# The power tree provides connectivity through all components from the VRM to +# the device. + +VRM = "U1" +OUTPUT_NET = "AVCC_1V3" +powertree_df, component_list_columns, net_group = edb.nets.get_powertree(OUTPUT_NET, GROUND_NETS) + +# Print some information about the power tree. + +print_columns = ["refdes", "pin_name", "component_partname"] +ncol = [component_list_columns.index(c) for c in print_columns] + +# This prints the header. Replace "pin_name" with "pin" to +# make the header align with the values. + +# + +print("\t".join(print_columns).replace("pin_name", "pin")) + +for el in powertree_df: + s = "" + count = 0 + for e in el: + if count in ncol: + s += "{}\t".format(e) + count += 1 + s.rstrip() + print(s) +# - + +# ## Remove unused components +# +# Delete all RLC components that are connected with only one pin. +# The ``Edb.components.delete_single_pin_rlc()`` method +# provides a useful way to +# remove components that are not needed for the simulation. + +edb.components.delete_single_pin_rlc() + +# You can also remove unused components explicitly by name. + +edb.components.delete("C380") + +# Nets can also be removed explicitly. + +edb.nets.delete("PDEN") + +# Print the top and bottom elevation of the stackup obtained using +# the ``Edb.stackup.limits()`` method. + +s = 'Top layer name: "{top}", Elevation: {top_el:.2f} ' +s += 'mm\nBottom layer name: "{bot}", Elevation: {bot_el:2f} mm' +top, top_el, bot, bot_el = edb.stackup.limits() +print(s.format(top=top, top_el=top_el * 1e3, bot=bot, bot_el=bot_el * 1e3)) + +# ## Set up for SIwave DCIR analysis +# +# Create a voltage source and then set up a DCIR analysis. + +edb.siwave.create_voltage_source_on_net("U1", "AVCC_1V3", "U1", "GND", 1.3, 0, "V1") +edb.siwave.create_current_source_on_net("IC2", "NetD3_2", "IC2", "GND", 1.0, 0, "I1") +setup = edb.siwave.add_siwave_dc_analysis("myDCIR_4") +setup.use_dc_custom_settings = True +setup.set_dc_slider = 0 +setup.add_source_terminal_to_ground("V1", 1) + +# ## Solve +# +# Save the modifications and run the analysis in SIwave. + +edb.save_edb() +edb.nets.plot(None, "1_Top", plot_components_on_top=True) + + + +# ## Export results +# +# Export all quantities calculated from the DC-IR analysis. +# The following method runs SIwave in batch mode from the command line. +# Results are written to the edb folder. +# Un-commment following lines to analyze with SIwave and export results. + +#siw_file = edb.solve_siwave() +# outputs = edb.export_siwave_dc_results( +# siw_file, +# setup.name, +# ) + +# Close EDB. After EDB is closed, it can be opened by AEDT. + +edb.close_edb() + +# ## View Layout in SIwave +# +# The SIwave user interface can be visualized and manipulated +# using the SIwave user interface. This command works on Window OS only. + +# + +# siwave = pyedb.Siwave("2024.2") +# siwave.open_project(siwave_file) +# report_file = os.path.join(temp_folder,'Ansys.htm') + +# siwave.export_siwave_report("myDCIR_4", report_file) +# siwave.close_project() +# siwave.quit_application() +# - + +# Clean up the temporary files and directory. + +temp_dir.cleanup() diff --git a/examples/00_edb/use_configuration/_static/configurator.png b/examples/00_edb/use_configuration/_static/configurator.png new file mode 100644 index 000000000..31b5a17ce Binary files /dev/null and b/examples/00_edb/use_configuration/_static/configurator.png differ diff --git a/examples/00_edb/use_configuration/_static/configurator_2.png b/examples/00_edb/use_configuration/_static/configurator_2.png new file mode 100644 index 000000000..ad82747b4 Binary files /dev/null and b/examples/00_edb/use_configuration/_static/configurator_2.png differ diff --git a/examples/00_edb/use_configuration/_static/parametrized_design.png b/examples/00_edb/use_configuration/_static/parametrized_design.png new file mode 100644 index 000000000..80bcb84b1 Binary files /dev/null and b/examples/00_edb/use_configuration/_static/parametrized_design.png differ diff --git a/examples/00_edb/use_configuration/dcir.py b/examples/00_edb/use_configuration/dcir.py new file mode 100644 index 000000000..627837561 --- /dev/null +++ b/examples/00_edb/use_configuration/dcir.py @@ -0,0 +1,155 @@ +# # DCIR Setup Leveraging EDB Configuration Format + +# ## Import the required packages + +# + +import json +import os +import tempfile + +from ansys.aedt.core import Hfss3dLayout +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False +# - + +# Download the example BGA Package design. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source=r"edb/BGA_Package.aedb", destination=temp_folder.name) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create config file + +# Define Component with solderballs. + +cfg_components = [ + { + "reference_designator": "FCHIP", + "part_type": "other", + "solder_ball_properties": { + "shape": "cylinder", + "diameter": "0.1mm", + "height": "0.085mm", + "chip_orientation": "chip_up", + }, + "port_properties": { + "reference_offset": 0, + "reference_size_auto": False, + "reference_size_x": 0, + "reference_size_y": 0, + }, + }, + { + "reference_designator": "BGA", + "part_type": "io", + "solder_ball_properties": { + "shape": "cylinder", + "diameter": "0.45mm", + "height": "0.45mm", + "chip_orientation": "chip_down", + }, + "port_properties": { + "reference_offset": 0, + "reference_size_auto": False, + "reference_size_x": 0, + "reference_size_y": 0, + }, + }, +] + +# Define Pin Groups on Components. + +cfg_pin_groups = [ + {"name": "BGA_VSS", "reference_designator": "BGA", "net": "VSS"}, + {"name": "BGA_VDD", "reference_designator": "BGA", "net": "VDD"}, +] + +# Define sources. + +cfg_sources = [ + { + "name": "FCHIP_Current", + "reference_designator": "FCHIP", + "type": "current", + "magnitude": 2, + "distributed": True, + "positive_terminal": {"net": "VDD"}, + "negative_terminal": {"nearest_pin": {"reference_net": "VSS", "search_radius": 5e-3}}, + }, + { + "name": "BGA_Voltage", + "reference_designator": "BGA", + "type": "voltage", + "magnitude": 1, + "positive_terminal": {"pin_group": "BGA_VDD"}, + "negative_terminal": {"pin_group": "BGA_VSS"}, + }, +] + +# Define DCIR setup. + +cfg_setups = [ + { + "name": "siwave_dc", + "type": "siwave_dc", + "dc_slider_position": 1, + "dc_ir_settings": {"export_dc_thermal_data": True}, + } +] + +# Create final configuration. + +cfg = { + "components": cfg_components, + "sources": cfg_sources, + "pin_groups": cfg_pin_groups, + "setups": cfg_setups, +} + +# Create the config file. + +file_json = os.path.join(temp_folder.name, "edb_configuration.json") +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Apply Config file + +# Apply configuration to the example layout + +edbapp.configuration.load(config_file=file_json) +edbapp.configuration.run() + +# Save and close EDB. + +edbapp.save() +edbapp.close() + +# The configured EDB file is saved in a temp folder. + +print(temp_folder.name) + +# ## Load edb into HFSS 3D Layout. + +h3d = Hfss3dLayout(edbapp.edbpath, version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True) + +# Solve. + +h3d.analyze(setup="siwave_dc") + +# Plot insertion loss. + +h3d.post.create_fieldplot_layers_nets(layers_nets=[["VDD_C1", "VDD"]], quantity="Voltage", setup="siwave_dc") + +# Shut Down Electronics Desktop + +h3d.close_desktop() + +# All project files are saved in the folder ``temp_file.dir``. If you've run this example as a Jupyter notebook you +# can retrieve those project files. diff --git a/examples/00_edb/use_configuration/import_components.py b/examples/00_edb/use_configuration/import_components.py new file mode 100644 index 000000000..b0dee6e9a --- /dev/null +++ b/examples/00_edb/use_configuration/import_components.py @@ -0,0 +1,121 @@ +# # Import Component Definitions +# This example shows how to import component definitions. It includes +# +# - Download an example board +# - Create a config file with component RLC and solder ball information +# - Apply the config file to the example board + +# ## Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from IPython.display import display +from ansys.aedt.core.downloads import download_file +import pandas as pd + +from pyedb import Edb + +AEDT_VERSION = "2024.2" + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys").name +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder) + +# ## Load example layout. + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create a config file with component information + +# Keywords +# +# - **reference_designator**. Reference designator of the component. +# - **part_type**. Part type of the component. Supported types are 'resistor', 'inductor', 'capacitor', 'ic', 'io', +# 'other'. +# - **enabled**. Only available for RLC components. +# - **solder_ball_properties**. +# - **shape**. Supported types are 'cylinder', 'spheroid', 'none'. +# - **diameter**. +# - **mid_diameter**. +# - **height**. +# - **material**. +# - **port_properties**. +# - **reference_offset**. +# - **reference_size_auto**. +# - **reference_size_x**. +# - **reference_size_y**. +# - **pin_pair_model**. RLC network. Multiple pin pairs are supported. +# - **first_pin**. First pin of the pin pair. +# - **second_pin**. Second pin of the pin pair. +# - **is_parallel**. +# - **resistance**. +# - **resistance_enabled**. +# - **inductance**. +# - **inductance_enabled**. +# - **capacitance**. +# - **capacitance_enabled**. + +cfg = dict() +cfg["components"] = [ + { + "reference_designator": "U1", + "part_type": "io", + "solder_ball_properties": { + "shape": "spheroid", + "diameter": "244um", + "mid_diameter": "400um", + "height": "300um", + "material": "air", + }, + "port_properties": { + "reference_offset": "0.1mm", + "reference_size_auto": True, + "reference_size_x": 0, + "reference_size_y": 0, + }, + }, + { + "reference_designator": "C375", + "enabled": False, + "pin_pair_model": [ + { + "first_pin": "1", + "second_pin": "2", + "is_parallel": False, + "resistance": "10ohm", + "resistance_enabled": True, + "inductance": "1nH", + "inductance_enabled": False, + "capacitance": "10nF", + "capacitance_enabled": True, + } + ], + }, +] + +display(pd.DataFrame(data=[cfg["components"][0]["solder_ball_properties"]])) + +display(pd.DataFrame(data=[cfg["components"][0]["port_properties"]])) + +display(pd.DataFrame(data=cfg["components"][1]["pin_pair_model"])) + +cfg_file_path = Path(temp_folder) / "cfg.json" +with open(cfg_file_path, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# Load config file + +edbapp.configuration.load(cfg_file_path, apply_file=True) + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_material.py b/examples/00_edb/use_configuration/import_material.py new file mode 100644 index 000000000..efbd65d08 --- /dev/null +++ b/examples/00_edb/use_configuration/import_material.py @@ -0,0 +1,81 @@ +# # Import Materials +# This example shows how to import materials. + +# ### Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from IPython.display import display +from ansys.aedt.core.downloads import download_file +import pandas as pd + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout. + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Review materials from layout + +# Get materials from layout in a dictionary. Materials are exported together with stadckup. + +data_cfg = edbapp.configuration.get_data_from_db(stackup=True) + + +df = pd.DataFrame(data=data_cfg["stackup"]["materials"]) +display(df) + +# ## Add a new material + +data_cfg["stackup"]["materials"].append( + {"name": "soldermask", "permittivity": 3.3, "dielectric_loss_tangent": 0.02}, +) + +# ## Edit existing material properties + +data_cfg["stackup"]["materials"][1]["name"] = "fr4_epoxy" +data_cfg["stackup"]["materials"][1]["dielectric_loss_tangent"] = 0.015 + +# ## Review modified materials + +df = pd.DataFrame(data=data_cfg["stackup"]["materials"]) +display(df) + +# ## Write material definition into a json file + +file_cfg = Path(temp_folder.name) / "edb_configuration.json" +with open(file_cfg, "w") as f: + json.dump(data_cfg, f, indent=4, ensure_ascii=False) + + +# ## Load materials from json configuration file + +edbapp.configuration.load(str(file_cfg), apply_file=True) + +# ## Review materials from layout + +edbapp.materials.materials + +# ## Check modified material properties + +edbapp.materials["fr4_epoxy"].loss_tangent + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_padstack_definitions.py b/examples/00_edb/use_configuration/import_padstack_definitions.py new file mode 100644 index 000000000..3ea55a4e4 --- /dev/null +++ b/examples/00_edb/use_configuration/import_padstack_definitions.py @@ -0,0 +1,138 @@ +# # Import Padstack Definitions +# This example shows how to import padstack definitions. This includes +# +# - Download an example board +# - Create a config file with hole information +# - Create a config file with pad and anti-pad information + +# ## Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from IPython.display import display +from ansys.aedt.core.downloads import download_file +import pandas as pd + +from pyedb import Edb + +AEDT_VERSION = "2024.2" + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys").name +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder) + +# ## Load example layout. + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create a config file with hole information + +# Keywords +# +# - **name**. Name of the padstack definition. +# - **hole_plating_thickness**. Hole plating thickness. +# - **hole_range**. Supported types are 'through', 'begin_on_upper_pad', 'end_on_lower_pad', 'upper_pad_to_lower_pad'. +# - **hole_parameters**. +# - **shape**. Supported types are 'circle', 'square', 'rectangle'. +# - Other parameters are shape dependent. +# - When shape is 'circle', supported parameter si 'diameter'. +# - When shape is 'square', supported parameter is 'size'. +# - When shape is 'rectangle', supported parameters are 'x_size', 'y_size'. + +cfg = dict() +cfg["padstacks"] = {} +cfg["padstacks"]["definitions"] = [ + { + "name": "v35h15", + "hole_plating_thickness": "25um", + "material": "copper", + "hole_range": "through", + "hole_parameters": { + "shape": "circle", + "diameter": "0.15mm", + }, + } +] + +df = pd.DataFrame(data=cfg["padstacks"]["definitions"]) +display(df) + +cfg_file_path = Path(temp_folder) / "cfg.json" +with open(cfg_file_path, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# Load config file + +edbapp.configuration.load(cfg_file_path, apply_file=True) + +# ## Create a config file with pad information + +# Keywords +# +# - **name**. Name of the padstack definition. +# - **pad_parameters**. +# - **regular_pad**. List of pad definition per layer. +# - **layer_name**. Name of the layer. +# - **shape**. Supported types are 'circle', 'square', 'rectangle', 'oval', 'bullet'. +# - Other parameters are shape dependent. +# - When shape is 'circle', supported parameter si 'diameter'. +# - When shape is 'square', supported parameter is 'size'. +# - When shape is 'rectangle', supported parameters are 'x_size', 'y_size'. +# - When shape is 'oval', supported parameters are 'x_size', 'y_size', 'corner_radius'. +# - When shape is 'bullet', supported parameters are 'x_size', 'y_size', 'corner_radius'. + +cfg = dict() +cfg["padstacks"] = {} +cfg["padstacks"]["definitions"] = [ + { + "name": "v35h15", + "pad_parameters": { + "regular_pad": [ + { + "layer_name": "1_Top", + "shape": "circle", + "offset_x": "0.1mm", + "offset_y": "0.1mm", + "rotation": "0", + "diameter": "0.5mm", + }, + { + "layer_name": "Inner1(GND1)", + "shape": "square", + "offset_x": "0.1mm", + "offset_y": "0.1mm", + "rotation": "45deg", + "size": "0.5mm", + }, + ], + "anti_pad": [{"layer_name": "1_Top", "shape": "circle", "diameter": "1mm"}], + }, + } +] + +df = pd.DataFrame(data=cfg["padstacks"]["definitions"][0]["pad_parameters"]["regular_pad"]) +display(df) + +df = pd.DataFrame(data=cfg["padstacks"]["definitions"][0]["pad_parameters"]["anti_pad"]) +display(df) + +cfg_file_path = Path(temp_folder) / "cfg.json" +with open(cfg_file_path, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# Load config file + +edbapp.configuration.load(cfg_file_path, apply_file=True) + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace edbapp.save() with +# edbapp.save_as("C:/example.aedb") to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_ports.py b/examples/00_edb/use_configuration/import_ports.py new file mode 100644 index 000000000..748faf3a2 --- /dev/null +++ b/examples/00_edb/use_configuration/import_ports.py @@ -0,0 +1,161 @@ +# # Import Ports +# This example shows how to import ports. In this example, we are going to +# +# - Download an example board +# - Create a configuration file +# - Add a circuit port between two nets +# - Add a circuit port between two pins +# - Add a circuit port between two pin groups +# - Add a circuit port between two coordinates +# - Add a coax port +# - Add a port reference to the nearest pin +# - Add distributed ports +# - Import the configuration file + +# ## Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create an empty dictionary to host all configurations + +cfg = dict() + +# ## Add a circuit port between two nets + +# Keywords +# +# - **name**. Name of the port. +# - **Reference_designator**. Reference designator of the component. +# - **type**. Type of the port. Supported types are 'circuit', 'coax' +# - **positive_terminal**. Positive terminal of the port. Supported types are 'net', 'pin', 'pin_group', 'coordinates' +# - **negative_terminal**. Negative terminal of the port. Supported types are 'net', 'pin', 'pin_group', 'coordinates', +# 'nearest_pin' + +port_1 = { + "name": "port_1", + "reference_designator": "X1", + "type": "circuit", + "positive_terminal": {"net": "PCIe_Gen4_TX2_N"}, + "negative_terminal": {"net": "GND"}, +} + +# ## Add a circuit port between two pins + +port_2 = { + "name": "port_2", + "reference_designator": "C375", + "type": "circuit", + "positive_terminal": {"pin": "1"}, + "negative_terminal": {"pin": "2"}, +} + +# ## Add a circuit port between two pin groups + +pin_groups = [ + {"name": "U9_5V_1", "reference_designator": "U9", "pins": ["32", "33"]}, + {"name": "U9_GND", "reference_designator": "U9", "net": "GND"}, +] + +port_3 = { + "name": "port_3", + "type": "circuit", + "positive_terminal": {"pin_group": "U9_5V_1"}, + "negative_terminal": {"pin_group": "U9_GND"}, +} + +# ## Add a circuit port between two coordinates + +# Keywords +# +# - **layer**. Layer on which the terminal is placed +# - **point**. XY coordinate the terminal is placed +# - **net**. Name of the net the terminal is placed on + +port_4 = { + "name": "port_4", + "type": "circuit", + "positive_terminal": {"coordinates": {"layer": "1_Top", "point": ["104mm", "37mm"], "net": "AVCC_1V3"}}, + "negative_terminal": {"coordinates": {"layer": "Inner6(GND2)", "point": ["104mm", "37mm"], "net": "GND"}}, +} + +# ## Add a coax port + +port_5 = {"name": "port_5", "reference_designator": "U1", "type": "coax", "positive_terminal": {"pin": "AM17"}} + +# ## Add a port reference to the nearest pin + +# Keywords +# +# - **reference_net**. Name of the reference net +# - **search_radius**. Reference pin search radius in meter + +port_6 = { + "name": "port_6", + "reference_designator": "U15", + "type": "circuit", + "positive_terminal": {"pin": "D7"}, + "negative_terminal": {"nearest_pin": {"reference_net": "GND", "search_radius": 5e-3}}, +} + +# ## Add distributed ports + +# Keywords +# +# - **distributed**. Whether to create distributed ports. When set to True, ports are created per pin + +ports_distributed = { + "name": "ports_d", + "reference_designator": "U7", + "type": "circuit", + "distributed": True, + "positive_terminal": {"net": "VDD_DDR"}, + "negative_terminal": {"net": "GND"}, +} + +# ## Add setups in configuration + +cfg["pin_groups"] = pin_groups +cfg["ports"] = [port_1, port_2, port_3, port_4, port_5, port_6, ports_distributed] + +# ## Write configuration into as json file + +file_json = Path(temp_folder.name) / "edb_configuration.json" +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Import configuration into example layout + +edbapp.configuration.load(config_file=file_json) +edbapp.configuration.run() + +# ## Review + +edbapp.ports + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_setup_ac.py b/examples/00_edb/use_configuration/import_setup_ac.py new file mode 100644 index 000000000..f494ec0cc --- /dev/null +++ b/examples/00_edb/use_configuration/import_setup_ac.py @@ -0,0 +1,142 @@ +# # Import Setup AC +# This example shows how to import SIwave, HFSS setups for AC analysis. In this example, we are going to +# +# - Download an example board +# - Create a configuration file +# - add setups +# - Import the configuration file + +# ### Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout. + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create an empty dictionary to host all configurations. + +cfg = dict() + +# ## Create an SIwave SYZ setup + +# Keywords +# +# - **name**. Name of the setup. +# - **type**. Type of the analysis setup. Supported types are 'siwave_ac', 'siwave_dc', 'hfss'. +# - **pi_slider_position**. PI slider position. Supported values are from '0', '1', '2'. 0:speed, 1:balanced, +# 2:accuracy. +# - **freq_sweep**. List of frequency sweeps. +# - **name**. Name of the sweep. +# - **type**. Type of the sweep. Supported types are 'interpolation', 'discrete', 'broadband'. +# - **frequencies**. Frequency distribution. +# - **distribution**. Supported distributions are 'linear_count', 'linear_scale', 'log_scale'. +# - **start**. Start frequency. Example, 1e6, "1MHz". +# - **stop**. Stop frequency. Example, 1e9, "1GHz". +# - **increment**. + +siwave_setup = { + "name": "siwave_1", + "type": "siwave_ac", + "pi_slider_position": 1, + "freq_sweep": [ + { + "name": "Sweep1", + "type": "interpolation", + "frequencies": [{"distribution": "log_scale", "start": 1e6, "stop": 1e9, "increment": 20}], + } + ], +} + +# ## Create a HFSS setup + +# Keywords +# +# - **name**. Name of the setup. +# - **type**. Type of the analysis setup. Supported types are 'siwave_ac', 'siwave_dc', 'hfss'. +# - **f_adapt**. Adaptive frequency. +# - **max_num_passes**. Maximum number of passes. +# - **max_mag_delta_s**. Convergence criteria delta S. +# - **mesh_operations**. Mesh operations. +# - **name**. Name of the mesh operation. +# - **type**. Type of the mesh operation. The supported types are 'base', 'length', 'skin_depth'. +# - **max_length**. Maximum length of elements. +# - **restrict_length**. Whether to restrict length of elements. +# - **refine_inside**. Whether to turn on refine inside objects. +# - **nets_layers_list**. {'layer_name':['net_name_1', 'net_name_2']} +# - **freq_sweep**. List of frequency sweeps. +# - **name**. Name of the sweep. +# - **type**. Type of the sweep. Supported types are 'interpolation', 'discrete', 'broadband'. +# - **frequencies**. Frequency distribution. +# - **distribution**. Supported distributions are 'linear_count', 'linear_scale', 'log_scale'. +# - **start**. Start frequency. Example, 1e6, "1MHz". +# - **stop**. Stop frequency. Example, 1e9, "1GHz". +# - **increment**. + +hfss_setup = { + "name": "hfss_1", + "type": "hfss", + "f_adapt": "5GHz", + "max_num_passes": 10, + "max_mag_delta_s": 0.02, + "mesh_operations": [ + { + "name": "mop_1", + "type": "length", + "max_length": "3mm", + "restrict_length": True, + "refine_inside": False, + "nets_layers_list": {"GND": ["1_Top", "16_Bottom"]}, + } + ], + "freq_sweep": [ + { + "name": "Sweep1", + "type": "interpolation", + "frequencies": [{"distribution": "log_scale", "start": 1e6, "stop": 1e9, "increment": 20}], + } + ], +} + +# ## Add setups in configuration + +cfg["setups"] = [siwave_setup, hfss_setup] + +# ## Write configuration into as json file + +file_json = Path(temp_folder.name) / "edb_configuration.json" +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Import configuration into example layout + +edbapp.configuration.load(config_file=file_json) +edbapp.configuration.run() + +# ## Review + +edbapp.setups + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_sources.py b/examples/00_edb/use_configuration/import_sources.py new file mode 100644 index 000000000..9e1d5f3c3 --- /dev/null +++ b/examples/00_edb/use_configuration/import_sources.py @@ -0,0 +1,170 @@ +# # Import Sources +# This example shows how to import voltage and current sources. In this example, we are going to +# +# - Download an example board +# - Create a configuration file +# - Add a voltage source between two nets +# - Add a current source between two pins +# - Add a current source between two pin groups +# - Add a current source between two coordinates +# - Add a current source to the nearest pin +# - Add distributed sources +# - Import the configuration file + +# ## Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create an empty dictionary to host all configurations + +cfg = dict() + +# ## Add a voltage source between two nets + +# Keywords +# +# - **name**. Name of the voltage source. +# - **Reference_designator**. Reference designator of the component. +# - **type**. Type of the source. Supported types are 'voltage', 'current' +# - **positive_terminal**. Supported types are 'net', 'pin', 'pin_group', 'coordinates' +# - **contact_radius**. Optional. Set circular equipotential region. +# - **inline**. Optional. When True, contact points are place in a row. +# - **num_of_contact**. Optional. Number of contact points. Default is 1. Applicable only when inline is True. +# - **negative_terminal**. Supported types are 'net', 'pin', 'pin_group', 'coordinates' +# - **equipotential**. Set equipotential region on pins when True. + +voltage_source = { + "name": "V_SOURCE_5V", + "reference_designator": "U4", + "type": "voltage", + "magnitude": 1, + "positive_terminal": {"net": "5V", "contact_radius": "1mm"}, + "negative_terminal": {"net": "GND", "contact_radius": "1mm"}, + "equipotential": True, +} + +# ## Add a current source between two pins + +current_source_1 = { + "name": "I_CURRENT_1A", + "reference_designator": "J5", + "type": "current", + "magnitude": 10, + "positive_terminal": {"pin": "15"}, + "negative_terminal": {"pin": "14"}, +} + +# ## Add a current source between two pin groups + +pin_groups = [ + {"name": "IC2_5V", "reference_designator": "IC2", "pins": ["8"]}, + {"name": "IC2_GND", "reference_designator": "IC2", "net": "GND"}, +] + +current_source_2 = { + "name": "CURRENT_SOURCE_2", + "type": "current", + "positive_terminal": {"pin_group": "IC2_5V"}, + "negative_terminal": {"pin_group": "IC2_GND"}, +} + +# ## Add a current source between two coordinates + +# Keywords +# +# - **layer**. Layer on which the terminal is placed +# - **point**. XY coordinate the terminal is placed +# - **net**. Name of the net the terminal is placed on + +current_source_3 = { + "name": "CURRENT_SOURCE_3", + "type": "current", + "equipotential": True, + "positive_terminal": {"coordinates": {"layer": "1_Top", "point": ["116mm", "41mm"], "net": "5V"}}, + "negative_terminal": {"coordinates": {"layer": "Inner1(GND1)", "point": ["116mm", "41mm"], "net": "GND"}}, +} + +# ## Add a current source reference to the nearest pin + +# Keywords +# +# - **reference_net**. Name of the reference net +# - **search_radius**. Reference pin search radius in meter + +current_source_4 = { + "name": "CURRENT_SOURCE_4", + "reference_designator": "J5", + "type": "current", + "positive_terminal": {"pin": "16"}, + "negative_terminal": {"nearest_pin": {"reference_net": "GND", "search_radius": 5e-3}}, +} + +# ## Add distributed current sources + +# Keywords +# +# - **distributed**. Whether to create distributed sources. When set to True, ports are created per pin + +sources_distributed = { + "name": "DISTRIBUTED", + "reference_designator": "U2", + "type": "current", + "distributed": True, + "positive_terminal": {"net": "5V"}, + "negative_terminal": {"net": "GND"}, +} + +# ## Add setups in configuration + +cfg["pin_groups"] = pin_groups +cfg["sources"] = [ + voltage_source, + current_source_1, + current_source_2, + current_source_3, + current_source_4, + sources_distributed, +] + +# ## Write configuration into as json file + +file_json = Path(temp_folder.name) / "edb_configuration.json" +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Import configuration into example layout + +edbapp.configuration.load(config_file=file_json) +edbapp.configuration.run() + +# ## Review + +edbapp.siwave.sources + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/import_stackup.py b/examples/00_edb/use_configuration/import_stackup.py new file mode 100644 index 000000000..446d5f4e8 --- /dev/null +++ b/examples/00_edb/use_configuration/import_stackup.py @@ -0,0 +1,83 @@ +# # Import Stackup +# This example shows how to import stackup file. + +# ## Import the required packages + +# + +import json +from pathlib import Path +import tempfile + +from IPython.display import display +from ansys.aedt.core.downloads import download_file +import pandas as pd + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout. + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Review original stackup definition + +# Get original stackup definition in a dictionary. Alternatively, stackup definition can be exported in a json file by +# edbapp.configuration.export() + +data_cfg = edbapp.configuration.get_data_from_db(stackup=True) + + +df = pd.DataFrame(data=data_cfg["stackup"]["layers"]) +display(df) + +# ## Modify stackup + +# Modify top layer thickness + +data_cfg["stackup"]["layers"][0]["thickness"] = 0.00005 + +# Add a solder mask layer + +data_cfg["stackup"]["layers"].insert( + 0, {"name": "soler_mask", "type": "dielectric", "material": "Megtron4", "fill_material": "", "thickness": 0.00002} +) + +# Review modified stackup + +df = pd.DataFrame(data=data_cfg["stackup"]["layers"]) +display(df.head(3)) + +# Write stackup definition into a json file + +file_cfg = Path(temp_folder.name) / "edb_configuration.json" +with open(file_cfg, "w") as f: + json.dump(data_cfg, f, indent=4, ensure_ascii=False) + + +# ## Load stackup from json configuration file + +edbapp.configuration.load(file_cfg, apply_file=True) + +# Plot stackup + +edbapp.stackup.plot() + +# Check top layer thickness + +edbapp.stackup["1_Top"].thickness + +# ## Save and close Edb +# The temporary folder will be deleted once the execution of this script is finished. Replace **edbapp.save()** with +# **edbapp.save_as("C:/example.aedb")** to keep the example project. + +edbapp.save() +edbapp.close() diff --git a/examples/00_edb/use_configuration/index.rst b/examples/00_edb/use_configuration/index.rst new file mode 100644 index 000000000..598169848 --- /dev/null +++ b/examples/00_edb/use_configuration/index.rst @@ -0,0 +1,162 @@ +Use configuration +~~~~~~~~~~~~~~~~~ + +The following examples illustrate the use of configuration files in PyEDB. +PyAEDT offers a GUI which utilizes config file. Please refer to `Configure Layout extension`_ for details. + +.. _Configure Layout extension : https://aedt.docs.pyansys.com/version/stable/User_guide/pyaedt_extensions_doc/project/configure_edb.html + + +.. grid:: 2 + + .. grid-item-card:: Power Integrity PDN analysis + :padding: 2 2 2 2 + :link: pdn_analysis + :link-type: doc + + .. image:: _static/configurator_2.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Serdes Signal Integrity Setup + :padding: 2 2 2 2 + :link: serdes + :link-type: doc + + .. image:: _static/configurator_2.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: PCB Power Integrity DCIR analysys + :padding: 2 2 2 2 + :link: pcb_dc_ir + :link-type: doc + + .. image:: _static/configurator_2.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Package Power Integrity DCIR analysys + :padding: 2 2 2 2 + :link: dcir + :link-type: doc + + .. image:: _static/configurator_2.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Create Parametric Design + :padding: 2 2 2 2 + :link: post_layout_parametrize + :link-type: doc + + .. image:: _static/parametrized_design.png + :alt: Connector + :width: 250px + :height: 200px + :align: center + + Create automatically parametrized design. + + .. grid-item-card:: Stackup management + :padding: 2 2 2 2 + :link: import_stackup + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Material management + :padding: 2 2 2 2 + :link: import_material + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Ports setup + :padding: 2 2 2 2 + :link: import_ports + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Simulation setup management + :padding: 2 2 2 2 + :link: import_setup_ac + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + + .. grid-item-card:: Padstack Definition management + :padding: 2 2 2 2 + :link: import_padstack_definitions + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Components management + :padding: 2 2 2 2 + :link: import_components + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + + .. grid-item-card:: Sources management + :padding: 2 2 2 2 + :link: import_sources + :link-type: doc + + .. image:: _static/configurator.png + :alt: Configurator + :width: 250px + :height: 200px + :align: center + +.. toctree:: + :hidden: + + pdn_analysis + serdes + pcb_dc_ir + dcir + import_stackup + import_material + import_ports + import_setup_ac + import_padstack_definitions + import_components + import_sources + post_layout_parametrize + diff --git a/examples/00_edb/use_configuration/pcb_dc_ir.py b/examples/00_edb/use_configuration/pcb_dc_ir.py new file mode 100644 index 000000000..2cffbd509 --- /dev/null +++ b/examples/00_edb/use_configuration/pcb_dc_ir.py @@ -0,0 +1,323 @@ +# # Set up EDB for PCB DC IR Analysis +# This example shows how to set up the electronics database (EDB) for DC IR analysis from a single +# configuration file. + +# ## Import the required packages + +# + +import json +import os +import tempfile + +from ansys.aedt.core import Hfss3dLayout, Icepak +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create an empty dictionary to host all configurations + +cfg = dict() +cfg["sources"] = [] + +# ## Update stackup + +cfg["stackup"] = { + "layers": [ + {"name": "Top", "type": "signal", "material": "copper", "fill_material": "FR4_epoxy", "thickness": "0.035mm"}, + {"name": "DE1", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.1mm"}, + { + "name": "Inner1", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + {"name": "DE2", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.088mm"}, + { + "name": "Inner2", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + {"name": "DE3", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.1mm"}, + { + "name": "Inner3", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + { + "name": "FR4_epoxy-1mm", + "type": "dielectric", + "material": "FR4_epoxy", + "fill_material": "", + "thickness": "1mm", + }, + { + "name": "Inner4", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + {"name": "DE5", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.1mm"}, + { + "name": "Inner5", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + {"name": "DE6", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.088mm"}, + { + "name": "Inner6", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.017mm", + }, + {"name": "DE7", "type": "dielectric", "material": "FR4_epoxy", "fill_material": "", "thickness": "0.1mm"}, + { + "name": "Bottom", + "type": "signal", + "material": "copper", + "fill_material": "FR4_epoxy", + "thickness": "0.035mm", + }, + ] +} + +# ## Define voltage source + +cfg["sources"].append( + { + "name": "vrm", + "reference_designator": "U2", + "type": "voltage", + "magnitude": 1, + "positive_terminal": {"net": "1V0"}, + "negative_terminal": {"net": "GND"}, + } +) + +# ## Define current source + +cfg["sources"].append( + { + "name": "U1_1V0", + "reference_designator": "U1", + "type": "current", + "magnitude": 10, + "positive_terminal": {"net": "1V0"}, + "negative_terminal": {"net": "GND"}, + } +) + +# ## Define SIwave DC IR analysis setup + +cfg["setups"] = [ + { + "name": "siwave_1", + "type": "siwave_dc", + "dc_slider_position": 1, + "dc_ir_settings": {"export_dc_thermal_data": True}, + } +] + +# ## Define Cutout + +cfg["operations"] = { + "cutout": {"signal_list": ["1V0"], "reference_list": ["GND"], "extent_type": "ConvexHull", "expansion_size": "20mm"} +} + +# ## Define package for thermal analysis (optional) + +cfg["package_definitions"] = [ + { + "name": "package_1", + "component_definition": "ALTR-FBGA1517-Ansys", + "maximum_power": 0.5, + "therm_cond": 2, + "theta_jb": 3, + "theta_jc": 4, + "height": "1mm", + "apply_to_all": False, + "components": ["U1"], + }, +] + +# ## Write configuration into a JSON file + +file_json = os.path.join(temp_folder.name, "edb_configuration.json") +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Import configuration into example layout + +edbapp.configuration.load(config_file=file_json) + +# Apply configuration to EDB. + +edbapp.configuration.run() + +# Save and close EDB. + +edbapp.save() +edbapp.close() + +# The configured EDB file is saved in a temp folder. + +print(temp_folder.name) + +# ## Load edb into HFSS 3D Layout. + +h3d = Hfss3dLayout(edbapp.edbpath, version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True) + +# ## Prepare for electro-thermal analysis in Icepak (Optional) + +h3d.modeler.set_temperature_dependence(include_temperature_dependence=True, enable_feedback=True, ambient_temp=22) + +# ## Analyze + +h3d.analyze() + +# ## Plot DC voltage + +voltage = h3d.post.create_fieldplot_layers_nets( + layers_nets=[ + ["Inner2", "1V0"], + ], + quantity="Voltage", + setup="siwave_1", +) + +file_path_image = os.path.join(temp_folder.name, "voltage.jpg") +voltage.export_image( + full_path=file_path_image, + width=640, + height=480, + orientation="isometric", + display_wireframe=True, + selections=None, + show_region=True, + show_axis=True, + show_grid=True, + show_ruler=True, +) + +# ## Plot power density + +power_density = h3d.post.create_fieldplot_layers_nets( + layers_nets=[ + ["Inner2", "no-net"], + ], + quantity="Power Density", + setup="siwave_1", +) + +file_path_image = os.path.join(temp_folder.name, "power_density.jpg") +power_density.export_image( + full_path=file_path_image, + width=640, + height=480, + orientation="isometric", + display_wireframe=True, + selections=None, + show_region=True, + show_axis=True, + show_grid=True, + show_ruler=True, +) + +# ## Save HFSS 3D Layout project + +h3d.save_project() + +# ## Create an Icepak design + +ipk = Icepak(version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=False) + +# ## Create PCB + +pcb = ipk.create_ipk_3dcomponent_pcb( + compName="PCB_pyAEDT", + setupLinkInfo=[h3d.project_file, h3d.design_name, "siwave_1", True, True], + solutionFreq=None, + resolution=0, + extent_type="Bounding Box", + powerin="0", +) + +# ## Include pckage definition from Edb + +pcb.included_parts = "Device" + +# ## Adjust air region + +region = ipk.modeler["Region"] +faces = [f.id for f in region.faces] +ipk.assign_pressure_free_opening(assignment=faces, boundary_name="Outlet") + +# ## Setup mesh + +glob_msh = ipk.mesh.global_mesh_region +glob_msh.global_region.positive_z_padding_type = "Absolute Offset" +glob_msh.global_region.positive_z_padding = "50 mm" +glob_msh.global_region.negative_z_padding_type = "Absolute Offset" +glob_msh.global_region.negative_z_padding = "80 mm" + +glob_msh = ipk.mesh.global_mesh_region +glob_msh.manual_settings = True +glob_msh.settings["EnableMLM"] = True +glob_msh.settings["EnforeMLMType"] = "2D" +glob_msh.settings["2DMLMType"] = "Auto" +glob_msh.settings["MaxElementSizeY"] = "2mm" +glob_msh.settings["MaxElementSizeX"] = "2mm" +glob_msh.settings["MaxElementSizeZ"] = "3mm" +glob_msh.settings["MaxLevels"] = "2" + +glob_msh.update() + +# ## Place monitor + +cpu = ipk.modeler["PCB_pyAEDT_U1_device"] +m1 = ipk.monitor.assign_face_monitor( + face_id=cpu.top_face_z.id, + monitor_quantity="Temperature", + monitor_name="TemperatureMonitor1", +) + +# ## Create Icepak setup + +setup1 = ipk.create_setup(MaxIterations=10) + +# Add 2-way coupling to the setup + +ipk.assign_2way_coupling(number_of_iterations=1) + +# ## Save + +ipk.save_project() + +# ## Shut Down Electronics Desktop + +ipk.release_desktop() + +# All project files are saved in the folder ``temp_file.dir``. If you've run this example as a Jupyter notebook you +# can retrieve those project files. diff --git a/examples/00_edb/use_configuration/pdn_analysis.py b/examples/00_edb/use_configuration/pdn_analysis.py new file mode 100644 index 000000000..ef6a44014 --- /dev/null +++ b/examples/00_edb/use_configuration/pdn_analysis.py @@ -0,0 +1,178 @@ +# # Set up EDB for Power Distribution Network Analysis +# This example shows how to set up the electronics database (EDB) for power integrity analysis from a single +# configuration file. + +# ## Import the required packages + +import json + +# + +import os +import tempfile + +from ansys.aedt.core import Hfss3dLayout +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +download_file(source="touchstone", name="GRM32_DC0V_25degC_series.s2p", destination=temp_folder.name) +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create an empty dictionary to host all configurations + +cfg = dict() + +# ## Assign S-parameter model to capactitors. + +# Set S-parameter library path. + +cfg["general"] = {"s_parameter_library": os.path.join(temp_folder.name, "touchstone")} + +# Assign the S-parameter model. +# +# Keywords +# +# - **name**. Name of the S-parameter model in AEDT. +# - **component**_definition. Known as component part number of part name. +# - **file_path**. Touchstone file or full path to the touchstone file. +# - **apply_to_all**. When set to True, assign the S-parameter model to all components share the same +# component_definition. When set to False, Only components in "components" are assigned. +# - **components**. when apply_to_all=False, components in the list are assigned an S-parameter model. +# When apply_to_all=False, components in the list are NOT assigned. +# - **reference_net**. Reference net of the S-parameter model. + +cfg["s_parameters"] = [ + { + "name": "GRM32_DC0V_25degC_series", + "component_definition": "CAPC0603X33X15LL03T05", + "file_path": "GRM32_DC0V_25degC_series.s2p", + "apply_to_all": False, + "components": ["C110", "C206"], + "reference_net": "GND", + } +] + +# ## Define ports +# Create a circuit port between power and ground nets. +# +# Keywords +# +# - **name**. Name of the port. +# - **reference_desinator**. +# - **type**. Type of the port. Supported types are 'ciruict', 'coax'. +# - **positive_terminal**. Positive terminal of the port. Supported types are 'net', 'pin', 'pin_group', 'coordinates'. +# - **negative_terminal**. Positive terminal of the port. Supported types are 'net', 'pin', 'pin_group', 'coordinates'. + +cfg["ports"] = [ + { + "name": "port1", + "reference_designator": "U1", + "type": "circuit", + "positive_terminal": {"net": "1V0"}, + "negative_terminal": {"net": "GND"}, + } +] + +# ## Define SIwave SYZ analysis setup +# +# Keywords +# +# - **name**. Name of the setup. +# - **type**. Type of the analysis setup. Supported types are 'siwave_ac', 'siwave_dc', 'hfss'. +# - **pi_slider_position**. PI slider position. Supported values are from '0', '1', '2'. 0:speed, 1:balanced, +# 2:accuracy. +# - **freq_sweep**. List of frequency sweeps. +# - **name**. Name of the sweep. +# - **type**. Type of the sweep. Supported types are 'interpolation', 'discrete', 'broadband'. +# - **frequencies**. Frequency distribution. +# - **distribution**. Supported distributions are 'linear_count', 'linear_scale', 'log_scale'. +# - **start**. Start frequency. Example, 1e6, "1MHz". +# - **stop**. Stop frequency. Example, 1e9, "1GHz". +# - **increment**. + +cfg["setups"] = [ + { + "name": "siwave_syz", + "type": "siwave_ac", + "pi_slider_position": 1, + "freq_sweep": [ + { + "name": "Sweep1", + "type": "interpolation", + "frequencies": [{"distribution": "log_scale", "start": 1e6, "stop": 1e9, "increment": 20}], + } + ], + } +] + +# ## Define Cutout +# +# Keywords +# +# - **signal_list**. List of nets to be kept after cutout. +# - **reference_list**. List of nets as reference planes. +# - **extent_type**. Supported extend types are 'Conforming', 'ConvexHull', 'Bounding'. +# For optional input arguments, refer to method pyedb.Edb.cutout() + +cfg["operations"] = { + "cutout": { + "signal_list": ["1V0"], + "reference_list": ["GND"], + "extent_type": "ConvexHull", + } +} + +# ## Write configuration into as json file + +file_json = os.path.join(temp_folder.name, "edb_configuration.json") +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Import configuration into example layout + +edbapp.configuration.load(config_file=file_json) + +# Apply configuration to EDB. + +edbapp.configuration.run() + +# Save and close EDB. + +edbapp.save() +edbapp.close() + +# The configured EDB file is saved in a temp folder. + +print(temp_folder.name) + +# ## Load edb into HFSS 3D Layout. + +h3d = Hfss3dLayout(edbapp.edbpath, version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True) + +# ## Analyze + +h3d.analyze() + +# ## Plot impedance + +solutions = h3d.post.get_solution_data(expressions="Z(port1,port1)") +solutions.plot() + +# ## Shut Down Electronics Desktop + +h3d.close_desktop() + +# All project files are saved in the folder ``temp_file.dir``. If you've run this example as a Jupyter notebook you +# can retrieve those project files. diff --git a/examples/00_edb/use_configuration/post_layout_parametrize.py b/examples/00_edb/use_configuration/post_layout_parametrize.py new file mode 100644 index 000000000..3c1f081e6 --- /dev/null +++ b/examples/00_edb/use_configuration/post_layout_parametrize.py @@ -0,0 +1,113 @@ +# # EDB: parameterized design +# +# This example shows how to +# 1. Set up an HFSS project using SimulationConfiguration class. +# 2. Create automatically parametrized design. +# +# This image shows the layout created in this example: +# +# +# + +# ## Import dependencies. + +import tempfile + +import ansys.aedt.core + +import pyedb +from pyedb.misc.downloads import download_file + +# ## Create an instance of a pyedb.Edb object. + +# + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +target_aedb = download_file("edb/ANSYS-HSD_V1.aedb", destination=temp_dir.name) +print("Project is located in ", target_aedb) + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edb = pyedb.Edb(edbpath=target_aedb, edbversion=edb_version) +print("AEDB file is located in {}".format(target_aedb)) +# - + +# ## Prepare the layout for the simulation +# +# The ``new_simulation_configuration()`` method creates an instance of +# the ``SimulationConfiguration`` class. This class helps define all preprocessing steps +# required to set up the PCB for simulation. After the simulation configuration has been defined, +# they are applied to the EDB using the ``Edb.build_simulation()`` method. + +simulation_configuration = edb.new_simulation_configuration() +simulation_configuration.signal_nets = [ + "PCIe_Gen4_RX0_P", + "PCIe_Gen4_RX0_N", + "PCIe_Gen4_RX1_P", + "PCIe_Gen4_RX1_N", +] +simulation_configuration.power_nets = ["GND"] +simulation_configuration.components = ["X1", "U1"] +simulation_configuration.do_cutout_subdesign = True +simulation_configuration.start_freq = "OGHz" +simulation_configuration.stop_freq = "20GHz" +simulation_configuration.step_freq = "10MHz" + +# Now apply the simulation setup to the EDB. + +edb.build_simulation_project(simulation_configuration) + +# ## Parameterize +# +# The layout can automatically be set up to enable parametric studies. For example, the +# impact of antipad diameter or trace width on signal integrity performance may be invested +# parametrically. + +edb.auto_parametrize_design(layers=True, materials=True, via_holes=True, pads=True, antipads=True, traces=True) +edb.save_edb() +edb.close_edb() + +# ## Open project in AEDT +# +# All manipulations thus far have been executed using the EDB API, which provides fast, +# streamlined processing of layout data in non-graphical mode. The layout and simulation +# setup can be visualized by opening it using the 3D Layout editor in AEDT. +# +# Note that there may be some delay while AEDT is being launched. + +hfss = ansys.aedt.core.Hfss3dLayout( + projectname=target_aedb, + specified_version=edb_version, + non_graphical=False, + new_desktop_session=True, +) + +# The following cell can be used to ensure that the design is valid for simulation. + +validation_info = hfss.validate_full_design() +is_ready_to_simulate = True + +# + +for s in validation_info[0]: + if "error" in s: + print(s) + is_ready_to_simulate = False + +if is_ready_to_simulate: + print("The model is ready for simulation.") +else: + print("There are errors in the model that must be fixed.") +# - + +# ## Release the application from the Python kernel +# +# It is important to release the application from the Python kernel after +# execution of the script. The default behavior of the ``release_desktop()`` method closes all open +# projects and closes the application. +# +# If you want to continue working on the project in graphical mode +# after script execution, call the following method with both arguments set to ``False``. + +hfss.release_desktop(close_projects=True, close_desktop=True) +temp_dir.cleanup() # Remove the temporary folder and files. All data will be removd! diff --git a/examples/00_edb/use_configuration/serdes.py b/examples/00_edb/use_configuration/serdes.py new file mode 100644 index 000000000..537eba2e5 --- /dev/null +++ b/examples/00_edb/use_configuration/serdes.py @@ -0,0 +1,278 @@ +# # Set up EDB for Serdes channel S-parameter extraction + +# ## Import the required packages + +import json + +# + +import os +import tempfile + +from ansys.aedt.core import Hfss3dLayout +from ansys.aedt.core.downloads import download_file + +from pyedb import Edb + +AEDT_VERSION = "2024.2" +NG_MODE = False + +# - + +# Download the example PCB data. + +temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +file_edb = download_file(source="edb/ANSYS-HSD_V1.aedb", destination=temp_folder.name) +download_file(source="touchstone", name="GRM32_DC0V_25degC_series.s2p", destination=os.path.split(file_edb)[0]) + +# ## Load example layout + +edbapp = Edb(file_edb, edbversion=AEDT_VERSION) + +# ## Create config file + +cfg_general = {"anti_pads_always_on": True, "suppress_pads": True} + +# Define dielectric materials, stackup and surface roughness model. + +cfg_stackup = { + "materials": [ + {"name": "copper", "permittivity": 1, "conductivity": 58000000.0}, + {"name": "megtron4", "permittivity": 3.77, "dielectric_loss_tangent": 0.005}, + {"name": "solder_resist", "permittivity": 3.0, "dielectric_loss_tangent": 0.035}, + ], + "layers": [ + { + "name": "Top", + "type": "signal", + "material": "copper", + "fill_material": "solder_resist", + "thickness": "0.035mm", + "roughness": { + "top": {"model": "huray", "nodule_radius": "0.5um", "surface_ratio": "5"}, + "bottom": {"model": "huray", "nodule_radius": "0.5um", "surface_ratio": "5"}, + "side": {"model": "huray", "nodule_radius": "0.5um", "surface_ratio": "5"}, + "enabled": True, + }, + }, + {"name": "DE1", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.1mm"}, + {"name": "Inner1", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "DE2", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.088mm"}, + {"name": "Inner2", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "DE3", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.1mm"}, + {"name": "Inner3", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "Megtron4-1mm", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": 0.001}, + {"name": "Inner4", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "DE5", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.1mm"}, + {"name": "Inner5", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "DE6", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.088mm"}, + {"name": "Inner6", "type": "signal", "material": "copper", "fill_material": "megtron4", "thickness": "0.017mm"}, + {"name": "DE7", "type": "dielectric", "material": "megtron4", "fill_material": "", "thickness": "0.1mm"}, + { + "name": "Bottom", + "type": "signal", + "material": "copper", + "fill_material": "solder_resist", + "thickness": "0.035mm", + }, + ], +} + +# Define Component with solderballs. + +cfg_components = [ + { + "reference_designator": "U1", + "part_type": "io", + "solder_ball_properties": {"shape": "cylinder", "diameter": "300um", "height": "300um"}, + "port_properties": { + "reference_offset": "0", + "reference_size_auto": True, + "reference_size_x": 0, + "reference_size_y": 0, + }, + } +] + +# Edit via padstack definition. Add backdrilling. + +cfg_padstacks = { + "definitions": [ + { + "name": "v40h15-2", + "material": "copper", + "hole_range": "upper_pad_to_lower_pad", + "hole_parameters": {"shape": "circle", "diameter": "0.2mm"}, + }, + { + "name": "v35h15-1", + "material": "copper", + "hole_range": "upper_pad_to_lower_pad", + "hole_parameters": {"shape": "circle", "diameter": "0.25mm"}, + }, + ], + "instances": [ + { + "name": "Via313", + "backdrill_parameters": { + "from_bottom": {"drill_to_layer": "Inner3", "diameter": "1mm", "stub_length": "0.2mm"} + }, + }, + { + "name": "Via314", + "backdrill_parameters": { + "from_bottom": {"drill_to_layer": "Inner3", "diameter": "1mm", "stub_length": "0.2mm"} + }, + }, + ], +} + +# Define ports. + +cfg_ports = [ + { + "name": "port_1", + "reference_designator": "U1", + "type": "coax", + "positive_terminal": {"net": "PCIe_Gen4_TX2_CAP_P"}, + }, + { + "name": "port_2", + "reference_designator": "U1", + "type": "coax", + "positive_terminal": {"net": "PCIe_Gen4_TX2_CAP_N"}, + }, + { + "name": "port_3", + "reference_designator": "X1", + "type": "circuit", + "positive_terminal": {"pin": "B8"}, + "negative_terminal": {"pin": "B7"}, + }, + { + "name": "port_4", + "reference_designator": "X1", + "type": "circuit", + "positive_terminal": {"pin": "B9"}, + "negative_terminal": {"pin": "B10"}, + }, +] + +# Define S-parameter assignment + +cfg_s_parameters = [ + { + "name": "cap_10nf", + "file_path": "$PROJECTDIR\\touchstone\\GRM32_DC0V_25degC_series.s2p", + "component_definition": "CAPC1005X55X25LL05T10", + "components": ["C375", "C376"], + "reference_net": "GND", + } +] + +# Define SIwave setup. + +cfg_setups = [ + { + "name": "siwave_setup", + "type": "siwave_ac", + "si_slider_position": 1, + "freq_sweep": [ + { + "name": "Sweep1", + "type": "interpolation", + "frequencies": [ + {"distribution": "linear_scale", "start": "50MHz", "stop": "20GHz", "increment": "50MHz"} + ], + } + ], + } +] + +# Define cutout. + +cfg_operations = { + "cutout": { + "signal_list": ["PCIe_Gen4_TX2_CAP_P", "PCIe_Gen4_TX2_CAP_N", "PCIe_Gen4_TX2_P", "PCIe_Gen4_TX2_N"], + "reference_list": ["GND"], + "custom_extent": [ + [0.014, 0.055], + [0.03674271504652968, 0.05493094625752912], + [0.07, 0.039], + [0.07, 0.034], + [0.05609890516829415, 0.03395233061637539], + [0.014, 0.044], + ], + } +} + +# Create final configuration. + +cfg = { + "general": cfg_general, + "stackup": cfg_stackup, + "components": cfg_components, + "padstacks": cfg_padstacks, + "ports": cfg_ports, + "s_parameters": cfg_s_parameters, + "setups": cfg_setups, + "operations": cfg_operations, +} + +# Create the config file. + +file_json = os.path.join(temp_folder.name, "edb_configuration.json") +with open(file_json, "w") as f: + json.dump(cfg, f, indent=4, ensure_ascii=False) + +# ## Apply Config file + +# Apply configuration to the example layout + +edbapp.configuration.load(config_file=file_json) +edbapp.configuration.run() + +edbapp.nets.plot(nets=[]) + +# Save and close EDB. + +edbapp.save() +edbapp.close() + +# The configured EDB file is saved in a temp folder. + +print(temp_folder.name) + +# ## Load edb into HFSS 3D Layout. + +h3d = Hfss3dLayout(edbapp.edbpath, version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True) + +# Create differential pair definition. + +h3d.set_differential_pair( + differential_mode="DIFF_BGA", + assignment="port_1", + reference="port_2", +) + +h3d.set_differential_pair( + differential_mode="DIFF_CONN", + assignment="port_3", + reference="port_4", +) + +# Solve. +# Un-comment to analyze SIwave. + +#h3d.analyze(setup="siwave_setup") + +# Plot insertion loss. + +# solutions = h3d.post.get_solution_data(expressions="mag(S(DIFF_CONN,DIFF_BGA))", context="Differential Pairs") +# solutions.plot(formula="db20") + +# Shut Down Electronics Desktop + +h3d.close_desktop() + +# All project files are saved in the folder ``temp_file.dir``. If you've run this example as a Jupyter notebook you +# can retrieve those project files. diff --git a/examples/aedt/hfss/index.rst b/examples/aedt/hfss/index.rst index e061adccd..7a5291b82 100644 --- a/examples/aedt/hfss/index.rst +++ b/examples/aedt/hfss/index.rst @@ -227,6 +227,19 @@ These examples use PyAEDT to show HFSS capabilities This example shows how to create a two-way coupling between HFSS and Icepak. + .. grid-item-card:: HFSS-Icepak microwave oven analysis + :padding: 2 2 2 2 + :link: microwave_oven + :link-type: doc + + .. image:: ../../hig_frequency/multiphysics/_static/oven.png + :alt: Microwave Oven + :width: 250px + :height: 200px + :align: center + + This example shows how to couple together HFSS and Icepak to run multiphysics + analysis on a well know problem of microwave oven. .. toctree:: :hidden: diff --git a/examples/aedt/icepak/index.rst b/examples/aedt/icepak/index.rst index 622602bc0..7444d5cd0 100644 --- a/examples/aedt/icepak/index.rst +++ b/examples/aedt/icepak/index.rst @@ -133,6 +133,19 @@ These examples use PyAEDT to show Icepak capabilities. This example uses PyAEDT to set up a simple Maxwell design consisting of a coil and a ferrite core. + .. grid-item-card:: HFSS-Icepak microwave oven analysis + :padding: 2 2 2 2 + :link: microwave_oven + :link-type: doc + + .. image:: ../../hig_frequency/multiphysics/_static/oven.png + :alt: Microwave Oven + :width: 250px + :height: 200px + :align: center + + This example shows how to couple together HFSS and Icepak to run multiphysics + analysis on a well know problem of microwave oven. .. toctree:: :hidden: diff --git a/examples/aedt_general/components/component_conversion.py b/examples/aedt_general/components/component_conversion.py index abe8bf402..80ed9af3c 100644 --- a/examples/aedt_general/components/component_conversion.py +++ b/examples/aedt_general/components/component_conversion.py @@ -1,109 +1,145 @@ -# # Encrypted 3D component conversion +# # Convert encrypted 3D components # -# This example shows how to convert an encrypted -# 3D component from ACIS to Parasolid in different AEDT releases. -# If you have models previous to Ansys AEDT 2023 R1 with an ACIS kernel, -# you can convert it to Parasolid. +# The 2023R1 release of Ansys Electronics Desktop (AEDT) implemented a new [solid modeling kernel](https://en.wikipedia.org/wiki/Geometric_modeling_kernel). +# +# This example demonstrates how to easily migrate encrypted +# 3D components from older versions of AEDT +# that relied on the ACIS modeling kernel, to the new +# versions of AEDT that employ +# the Parasolid kernel. Specifically, if your +# encrypted 3D +# components were created with version 22R2 or +# earlier, you'll need to convert them +# to a version ≥ 23R1 that supports the Parasolid modeler. # # Keywords: **HFSS**, **Encrypted**, **3D component**, **Modeler kernel**. - -# - -# ## Perform imports and define constants # -# Import the required packages. +# + +# ## Prerequisites # +# ### Perform imports +# + import os import tempfile import time -from pyaedt import Desktop, Hfss, settings -from pyedb.misc.downloads import download_file +from ansys.aedt.core import Desktop, Hfss, settings +from ansys.aedt.core.downloads import download_file +# - -# Define constants. +# ### Define constants +# Constants help ensure consistency and avoid repetition throughout the example. AEDT_VERSION = "2024.2" OLD_AEDT_VERSION = "2024.1" -NUM_CORES = 4 -NG_MODE = False # Open AEDT UI when it is launched. +NG_MODE = False # Open AEDT UI when AEDT is launched. -# ## Create temporary directory +# ### Create temporary directory +# +# Create a temporary working directory. +# The name of the working folder is stored in ``temp_folder.name``. # -# 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``. +# > **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. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") -# ## Download encrypted example +# ## Covert the encrypted component # -# Download the encrypted 3D component example. +# ### Retrieve the component that will be converted +# +# The ``download_file()`` method provides access to a library +# of examples and models from the Ansys GitHub organization: +# [example-data repository](https://github.com/ansys/example-data/tree/master/pyaedt). Download the "old" +# encrypted 3D component and define a name to use for the new, coverted component. +# + a3dcomp = download_file( - directory="component_3d", - filename="SMA_Edge_Connector_23r2_encrypted_password_ansys.a3dcomp", + source="component_3d", + name="SMA_Edge_Connector_23r2_encrypted_password_ansys.a3dcomp", destination=temp_folder.name, ) -# ## Enable multiple desktop support +# Name of the converted 3D component: +new_component_filename = os.path.join(temp_folder.name, r"SMA_Edge_Connector_encrypted.a3dcomp") +# - + +# ### Enable multiple desktop instances. +# +# This example runs two versions of AEDT simultaneously. +# +# > **Note:** Both the old and new versions of AEDT must be installed on the machine +# > where this example runs. settings.use_multi_desktop = True -# ## Prepare encrypted 3D component in ACIS +# ### Load the encrypted 3D component. # -# Launch the old AEDT release. +# Launch the old version of AEDT and load the encrypted component. +# Pass the keyword argument ``aedt_process_id`` to ensure that the ``Hfss`` +# instance connects to the correct running version of HFSS. The encryption +# password must be provided to enable conversion. +# + aedt_old = Desktop(new_desktop=True, version=OLD_AEDT_VERSION) # Insert an empty HFSS design. - hfss1 = Hfss(aedt_process_id=aedt_old.aedt_process_id, solution_type="Terminal") # Insert the encrypted 3D component. +cmp = hfss1.modeler.insert_3d_component(input_file=a3dcomp, password="ansys") -cmp = hfss1.modeler.insert_3d_component(comp_file=a3dcomp, password="ansys") - -# Open the 3D component in an HFSS design. - +# Open the 3D component definition. app_comp = cmp.edit_definition(password="ansys") +# - -# ## Create an encrypted 3D component in Parasolid +# ### Convert the encrypted 3D component +# +# Launch another instance of AEDT to enable conversion of the +# 3D component. # -# Launch the new AEDT release +# After the new version of AEDT is started, the process ID +# is retrieved via the property ``aedt.aedt_process_id`` and is passed +# as an argument to `Hfss()`. This ensures that the newly created +# `hfss2` object +# is connected to the +# correct version and instance of AEDT. -aedt = Desktop(new_desktop_session=True, specified_version=AEDT_VERSION) +# + +aedt = Desktop(new_desktop=True, version=AEDT_VERSION) # Insert an empty HFSS design. - hfss2 = Hfss(aedt_process_id=aedt.aedt_process_id, solution_type="Terminal") # Copy objects from the old design. - hfss2.copy_solid_bodies_from(design=app_comp, no_vacuum=False, no_pec=False) # Create the new encrypted 3D component. - hfss2.modeler.create_3dcomponent( - input_file=os.path.join(temp_folder.name, r"SMA_Edge_Connector_encrypted.a3dcomp"), + input_file=new_component_filename, is_encrypted=True, edit_password="ansys", hide_contents=False, allow_edit=True, password_type="InternalPassword", ) +# - -# ## Release AEDT +# ## Finish +# +# ### Save the projects aedt.save_project() aedt_old.save_project() aedt.release_desktop() aedt_old.release_desktop() +print(f"The new encrypted 3D component can be retrieved from: {new_component_filename}") # Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory. time.sleep(3) -# ## Clean up +# ### Clean up # # All project files are saved in the folder ``temp_folder.name``. # If you've run this example as a Jupyter notebook, you diff --git a/examples/aedt_general/components/reuse_component.py b/examples/aedt_general/components/reuse_component.py index b9e6a7f56..a0e63f900 100644 --- a/examples/aedt_general/components/reuse_component.py +++ b/examples/aedt_general/components/reuse_component.py @@ -1,201 +1,273 @@ # # 3D component creation and reuse - -# Here is a workflow for creating a 3D component and reusing it: -# -# Step 1: Create an antenna using PyAEDT and HFSS 3D Modeler. (The antenna can also be created using EDB and -# HFSS 3D Layout). -# -# Step 2. Store the object as a 3D component on the disk. -# -# Step 3. Reuse the 3D component in another project. # -# Step 4. Parametrize and optimize the target design. +# This example demonstrates how to create and use an HFSS 3D component by +# performing the following: +# 1. Create a patch antenna using the HFSS 3D Modeler. +# 2. Save the antenna as a 3D component on the disk. +# 3. Import multiple instances of patch antenna as +# a 3D component in a new project to create a small array. +# 5. Set up the new design for simulation and optimization. # -# Keywords: **AEDT**, **General**, **3D component**. +# Keywords: **AEDT**, **Antenna**, **3D component**. -# ## Perform imports and define constants -# Import the required packages. +# ## Prerequisites +# +# ### Perform imports +# + import os import tempfile import time from ansys.aedt.core import Hfss +# - -# Define constants. +# ### Define constants +# Constants help ensure consistency and avoid repetition throughout the example. AEDT_VERSION = "2024.2" NG_MODE = False # Open AEDT UI when it is launched. -# ## Create temporary directory +# ### 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``. +# 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. +# +# This example creates two projects defined in `project_names. +# The first will be used to +# create the patch antenna model and the 2nd project +# will be used to demonstrate the use 3D components. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") +project_names = [os.path.join(temp_folder.name, "start_project.aedt"), + os.path.join(temp_folder.name, "final_project.aedt"), + ] -# Create an HFSS object. +# ### Launch HFSS +# AEDT is started when an instance of the ``Hfss()`` class is +# instantiated. An HFSS design is automatically inserted in the +# AEDT project. hfss = Hfss( version=AEDT_VERSION, - new_desktop=True, + design="build_comp", + new_desktop=True, # Set to False if you want to connect to an existing AEDT session. close_on_exit=True, non_graphical=NG_MODE, solution_type="Modal", ) -hfss.save_project(os.path.join(temp_folder.name, "example.aedt")) +hfss.save_project(project_names[0]) -# ## Define variables +# ## Model preparation # -# PyAEDT can create and store all variables available in AEDT (such as design, project, -# and postprocessing). +# ### Define parameters +# +# Parameters can be defined in the HFSS design and subsequently +# used to optimiuze +# performance, run parametric studies or +# explore the impact of tolerance on performance. -hfss["thick"] = "0.1mm" +hfss["thickness"] = "0.1mm" hfss["width"] = "1mm" -# ## Create modeler objects +# ### Build the antenna model +# +# The compact, +# [pythonic syntax](https://docs.python-guide.org/writing/style/#code-style) +# allows you to create the model from simple +# primitives. This patch antenna is comprised of the FR-4 substrate, a rectangle, +# and the coaxial +# probe feed. Each primitive is of type ``Object3D``. # -# PyAEDT supports all modeler functionalities available in AEDT. -# You can create, delete, and modify objects using all available Boolean operations. -# PyAEDT can also fully access history. +# > **Note: ** The feed length of the patch antenna is fixed and is not +# > parametric in HFSS. # + substrate = hfss.modeler.create_box( - ["-width", "-width", "-thick"], - ["2*width", "2*width", "thick"], + ["-width", "-width", "-thickness"], + ["2*width", "2*width", "thickness"], material="FR4_epoxy", - name="sub", + name="substrate", ) +feed_length = "0.1mm" # This parameter is defined only in Python and is not varied + patch = hfss.modeler.create_rectangle( - "XY", ["-width/2", "-width/2", "0mm"], ["width", "width"], name="patch1" + "XY", ["-width/2", "-width/2", "0mm"], ["width", "width"], name="patch" ) -via1 = hfss.modeler.create_cylinder( +inner_conductor = hfss.modeler.create_cylinder( 2, - ["-width/8", "-width/4", "-thick"], + ["-width/8", "-width/4", f"-thickness - {feed_length}"], "0.01mm", - "thick", + f"thickness + {feed_length}", material="copper", name="via_inner", ) via_outer = hfss.modeler.create_cylinder( 2, - ["-width/8", "-width/4", "-thick"], + ["-width/8", "-width/4", "-thickness"], "0.025mm", - "thick", + f"-{feed_length}", material="Teflon_based", name="via_teflon", ) # - -# ## Assign bundaries -# -# Most of HFSS boundaries and excitations are already available in PyAEDT. -# You can easily assign a boundary to a face or to an object by taking advantage of -# Object-Oriented Programming (OOP) available in PyAEDT. - -# ### Assign Perfect E boundary to sheets +# ### Assign boundaries # -# Assign a Perfect E boundary to sheets. +# Boundary conditions can be assigned to faces or bodies in the model +# using methods of the ``Hfss`` class. -hfss.assign_perfecte_to_sheets(patch) +hfss.assign_perfecte_to_sheets(patch, name="patch_bc") -# ### Assign boundaries to faces +# ### Assign boundaries to the via # -# Assign boundaries to the top and bottom faces of an object. +# The following statement selects the outer surface of the cylinder +# ``via_outer``, excluding the upper and lower faces, and assigns +# the "perfect conductor" boundary condition. # + -side_face = [ - i - for i in via_outer.faces - if i.id not in [via_outer.top_face_z.id, via_outer.bottom_face_z.id] -] - -hfss.assign_perfecte_to_sheets(side_face) -hfss.assign_perfecte_to_sheets(substrate.bottom_face_z) +side_face = [i for i in via_outer.faces if i.id not in + [via_outer.top_face_z.id, via_outer.bottom_face_z.id] + ] + +hfss.assign_perfecte_to_sheets(side_face, name="feed_gnd") +hfss.assign_perfecte_to_sheets(substrate.bottom_face_z, name="ground_plane") +hfss.assign_perfecth_to_sheets(via_outer.top_face_z, name="feed_thru") # Ensure power flows through the ground plane. +hfss.change_material_override(material_override=True) # Allow the probe feed to extend outside the substrate. # - -# ## Create wave port +# ### Create wave port # -# You can assign a wave port to a sheet or to a face of an object. +# A wave port is assigned to the bottom face of the via. Note that the property `via_outer.bottom_face_z` +# is a ``FacePrimitive`` object. -hfss.wave_port( +p1 = hfss.wave_port( via_outer.bottom_face_z, name="P1", + create_pec_cap=True ) -# ## Create 3D component +# ### Query the object properties # -# Once the model is ready, you can create a 3D component. -# Multiple options are available to partially select objects, coordinate systems, -# boundaries, and mesh operations. You can also create encrypted 3D components. +# Everything in Python is an object. You can use the object +# properties to obtain detailed information as shown below: -component_path = os.path.join(temp_folder.name, "component_test.aedbcomp") -hfss.modeler.create_3dcomponent(component_path, "patch_antenna") +out_str = f"A port named '{p1.name}' was assigned to a surface object" +out_str += f" of type \n {type(via_outer.bottom_face_z)}\n" +out_str += f"which is located at the bottom surface of the object '{via_outer.name}'\n" +out_str += f"at the z-elevation: {via_outer.bottom_face_z.bottom_edge_z} " +out_str += f"{hfss.modeler.model_units}\n" +out_str += f"and has the face ID: {via_outer.bottom_face_z.id}." +print(out_str) -# ## Manage multiple project +# ## Create 3D component # -# PyAEDT lets you control multiple projects, designs, and solution types at the same time. +# You can now create a 3D component from the antenna model. The following statements +# save the component to the specified location with the name "patch_antenna". + +component_path = os.path.join(temp_folder.name, "patch_antenna.a3dcomp") +hfss.modeler.create_3dcomponent(component_path, name="patch_antenna") + +# A 2nd instance of HFSS is created to demonstrate how the new 3D component can be +# used within a new design. -new_project = os.path.join(temp_folder.name, "new_project.aedt") hfss2 = Hfss( version=AEDT_VERSION, - project=new_project, + project=project_names[1], design="new_design", solution_type="Modal", ) +hfss2.change_material_override(material_override=True) -# ## Insert 3D component -# -# You can insert a 3D component without supplying additional information. -# All needed information is read from the file itself. - -hfss2.modeler.insert_3d_component(component_path) - -# ## Parametrize 3D components +# ### Insert 3D components # -# You can specify parameters for any 3D components. +# Place 4 antennas to make a small array. +# - The substrate thickness is modified by creating the parameter "p_thick" and +# assigning it to the "thickness" parameter of the components. +# - The first antenna is placed at the origin. +# - The spacing between elements is defined by the parameter $2\times w$ -hfss2.modeler.user_defined_components["patch_antenna1"].parameters -hfss2["p_thick"] = "1mm" -hfss2.modeler.user_defined_components["patch_antenna1"].parameters["thick"] = "p_thick" +# + +# Define a parameter to use for the substrate thickness. +hfss2["p_thick"] = "0.2mm" + +# Define a parameter to specify the patch width. +hfss2["w"] = "1mm" + +# [x, y, z] location of the patch elements. +positions = [["2*w", "w", 0], ["-2*w", "w", 0], [0, "2.5*w", 0]] + +# Keep track of patch elements and their coordinate systems in Python lists: +elements = [] +cs = [] + +# The first patch is located at the origin. +elements.append(hfss2.modeler.insert_3d_component(component_path, name="patch_0")) +elements[0].parameters["thickness"] = "p_thick" +elements[0].parameters["width"] = "w" + +# Now place the other 3 patches: +count = 1 +for p in positions: + cs.append(hfss2.modeler.create_coordinate_system(origin=p, name="cs_" + str(count))) # Create the patch coordinate system. + elements.append(hfss2.modeler.insert_3d_component(component_path, # Place the patch element. + coordinate_system=cs[-1].name, + name="patch_" + str(count)) + ) + count +=1 + + elements[-1].parameters["thickness"] = "p_thick" + elements[-1].parameters["width"] = "w" +# - -# ## Insert multiple 3D components -# -# There is no limit to the number of 3D components that can be inserted in a design. -# These components can be the same or linked to different files. +# You can inspect the component parameters. -hfss2.modeler.create_coordinate_system(origin=[20, 20, 10], name="Second_antenna") -ant2 = hfss2.modeler.insert_3d_component( - component_path, coordinate_system="Second_antenna" -) +units = hfss2.modeler.model_units # Retrieve the length units as a string. +for e in elements: + print(f"Component '{e.name}' is located at (x={e.center[0]} {units}, y={e.center[1]} {units})") -# ## Move 3D components +# ### Move 3D components # -# Move a 3D component by either changing its position or moving the relative coordinate system. +# The position of each 3D component can be changed by modifying the ``origin`` +# of the corresponding coordinate system. -hfss2.modeler.coordinate_systems[0].origin = [10, 10, 3] +hfss2.modeler.coordinate_systems[0].origin = [0, "2*w", 0] -# ## Create air region +# ### Create air region # -# Create an air region and assign a boundary to a face or an object. +# The volume of the solution domain is defined +# by an air region object. The following cell creates the +# region object and assigns the radiation boundary to the outer surfaces of +# the region. -hfss2.modeler.create_air_region(30, 30, 30, 30, 30, 30) +hfss2.modeler.create_air_region( x_pos=2, y_pos=2, z_pos=2.5, x_neg=2, y_neg=2, z_neg=2, is_percentage=False) hfss2.assign_radiation_boundary_to_faces(hfss2.modeler["Region"].faces) -# ## Create setup and optimetrics analysis +# ### Create solution setup and optimetrics analysis # -# Once a project is ready to be solved, use PyAEDT to create a setup and parametrics analysis. -# All setup parameters can be edited. +# Once a project is ready to be solved, define the solution setup and parametric analysis. -setup1 = hfss2.create_setup() -optim = hfss2.parametrics.add("p_thick", "0.2mm", "1.5mm", step=14) +# + +setup1 = hfss2.create_setup(RangeStart="60GHz", RangeEnd="80GHz") +optim = hfss2.parametrics.add("w", start_point="0.8mm", + end_point="1.2mm", + step="0.05mm", + variation_type="LinearStep", + name="Sweep Patch Width") + +if hfss.valid_design: + print(f"The HFSS design '{hfss.design_name}' is ready to solve.") +else: + print(f"Something is not quite right.") +# - -# ## Plot objects +# ### Visualize the model hfss2.modeler.fit_all() hfss2.plot( @@ -204,18 +276,20 @@ plot_air_objects=True, ) -# ## Release AEDT +# ## Finish +# +# ### Save the project hfss2.save_project() hfss2.release_desktop() # Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory. time.sleep(3) -# ## Clean up +# ### 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. +# can retrieve those project files. The following cell +# removes all temporary files, including the project folder. temp_folder.cleanup() diff --git a/examples/aedt_general/report/automatic_report.py b/examples/aedt_general/report/automatic_report.py index 46ee62985..a5274824d 100644 --- a/examples/aedt_general/report/automatic_report.py +++ b/examples/aedt_general/report/automatic_report.py @@ -18,6 +18,7 @@ import ansys.aedt.core from IPython.display import Image +# - # Define constants. diff --git a/examples/electrothermal/coaxial_hfss_icepak.py b/examples/electrothermal/coaxial_hfss_icepak.py index f39c1fb03..27e9a0a77 100644 --- a/examples/electrothermal/coaxial_hfss_icepak.py +++ b/examples/electrothermal/coaxial_hfss_icepak.py @@ -253,7 +253,7 @@ hfss.save_project() ipk = ansys.aedt.core.Icepak(version=AEDT_VERSION) -ipk.solution_type = ipk.SOLUTIONS.Icepak.SteadyTemperatureAndFlow +ipk.solution_type = ipk.SOLUTIONS.Icepak.SteadyState ipk.modeler.fit_all() # ## Solve models diff --git a/examples/electrothermal/index.rst b/examples/electrothermal/index.rst index 7cd18edf2..dc5b701b3 100644 --- a/examples/electrothermal/index.rst +++ b/examples/electrothermal/index.rst @@ -133,6 +133,22 @@ These examples use PyAEDT to show electrothermal capabilities of AEDT This example uses PyAEDT to set up a simple Maxwell design consisting of a coil and a ferrite core. + .. grid-item-card:: HFSS-Icepak microwave oven analysis + :padding: 2 2 2 2 + :link: microwave_oven + :link-type: doc + + .. image:: ../hig_frequency/multiphysics/_static/oven.png + :alt: Microwave Oven + :width: 250px + :height: 200px + :align: center + + This example shows how to couple together HFSS and Icepak to run multiphysics + analysis on a well know problem of microwave oven. + + + .. toctree:: :hidden: diff --git a/examples/high_frequency/antenna/5G_antenna_parametrics.py b/examples/high_frequency/antenna/5G_antenna_parametrics.py new file mode 100644 index 000000000..15466d452 --- /dev/null +++ b/examples/high_frequency/antenna/5G_antenna_parametrics.py @@ -0,0 +1,384 @@ +# # EDB: Layout Components +# +# This example shows how you can use EDB to create a parametric component using +# 3D Layout and use it in HFSS 3D. + +# ## Perform required imports +# +# Perform required imports, which includes importing the ``Hfss3dlayout`` object +# and initializing it on version 2023 R2. + +# + +import os +import tempfile + +import ansys.aedt.core + +import pyedb + +# - + +# ## Set non-graphical mode + +non_graphical = False + +# ## Create data classes +# +# Data classes are useful to do calculations and store variables. +# There are three data classes: ``Patch``, ``Line``, and ``Array``. + + +# + +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, "-{}/2".format(self.height)], + ["{} + {}".format(self.position, self.width), "-{}/2".format(self.height)], + ["{} + {}".format(self.position, self.width), "{}/2".format(self.height)], + [self.position, "{}/2".format(self.height)], + ] + + +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, "-{}/2".format(self.width)], + ["{} + {}".format(self.position, self.length), "-{}/2".format(self.width)], + ["{} + {}".format(self.position, self.length), "{}/2".format(self.width)], + [self.position, "{}/2".format(self.width)], + ] + + +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, "-{}/2-1e-3".format(self.width)], + ["{}+1e-3".format(self.length), "-{}/2-1e-3".format(self.width)], + ["{}+1e-3".format(self.length), "{}/2+1e-3".format(self.width)], + [-1e-3, "{}/2+1e-3".format(self.width)], + ] + + +# - + +# ## Launch EDB +# +# PyEDB.Edb allows to open existing Edb project or create a new empty project. + +# + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +aedb_path = os.path.join(temp_dir.name, "linear_array.aedb") + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +# Create an instance of the Edb class. +edb = pyedb.Edb(edbpath=aedb_path, edbversion=edb_version) +# - + +# Add stackup layers +layers = { + "materials": {"copper_high_cond": {"conductivity": 60000000}}, + "layers": { + "TOP": {"type": "signal", "thicness": "35um", "material": "copper_high_cond"}, + "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"}, + }, +} + +edb.stackup.load(layers) + +# Create the first patch and feed line using the ``Patch``, ``Line``classes defined above. +# +# Define parameters: + +# + +edb["w1"] = 1.4e-3 +edb["h1"] = 1.2e-3 +edb["initial_position"] = 0.0 +edb["l1"] = 2.4e-3 +edb["trace_w"] = 0.3e-3 + +first_patch = Patch(width="w1", height="h1", position="initial_position") +edb.modeler.create_polygon(first_patch.points, "TOP", net_name="Array_antenna") +# - + +# First line + +first_line = Line(length="l1", width="trace_w", position=first_patch.width) +edb.modeler.create_polygon(first_line.points, "TOP", net_name="Array_antenna") + +# Now use the ``LinearArray`` class to create the array. + +# + +edb["w2"] = 2.29e-3 +edb["h2"] = 3.3e-3 +edb["l2"] = 1.9e-3 +edb["trace_w2"] = 0.2e-3 + +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 = "{} + {}".format(first_line.position, first_line.length) + +while current_patch <= linear_array.nbpatch: + patch.position = current_position + edb.modeler.create_polygon(patch.points, "TOP", net_name="Array_antenna") + current_position = "{} + {}".format(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 = "{} + {}".format(current_position, line.length) + current_patch += 1 + +linear_array.length = current_position +# - + +# Add the ground conductor. + +edb.modeler.create_polygon(linear_array.points, "GND", net_name="GND") + +# Add the connector pin to use to assign the port. + +edb.padstacks.create(padstackname="Connector_pin", holediam="100um", paddiam="0", antipaddiam="200um") +con_pin = edb.padstacks.place( + ["{}/4.0".format(first_patch.width), 0], + "Connector_pin", + net_name="Array_antenna", + fromlayer="TOP", + tolayer="GND", + via_name="coax", +) + +# Add a connector ground. + +edb.modeler.create_polygon(first_patch.points, "Virt_GND", net_name="GND") +edb.padstacks.create("gnd_via", "100um", "0", "0") +edb["via_spacing"] = 0.2e-3 +con_ref1 = edb.padstacks.place( + [ + "{} + {}".format(first_patch.points[0][0], "via_spacing"), + "{} + {}".format(first_patch.points[0][1], "via_spacing"), + ], + "gnd_via", + fromlayer="GND", + tolayer="Virt_GND", + net_name="GND", +) +con_ref2 = edb.padstacks.place( + [ + "{} + {}".format(first_patch.points[1][0], "-via_spacing"), + "{} + {}".format(first_patch.points[1][1], "via_spacing"), + ], + "gnd_via", + fromlayer="GND", + tolayer="Virt_GND", + net_name="GND", +) +con_ref3 = edb.padstacks.place( + [ + "{} + {}".format(first_patch.points[2][0], "-via_spacing"), + "{} + {}".format(first_patch.points[2][1], "-via_spacing"), + ], + "gnd_via", + fromlayer="GND", + tolayer="Virt_GND", + net_name="GND", +) +con_ref4 = edb.padstacks.place( + [ + "{} + {}".format(first_patch.points[3][0], "via_spacing"), + "{} + {}".format(first_patch.points[3][1], "-via_spacing"), + ], + "gnd_via", + fromlayer="GND", + tolayer="Virt_GND", + net_name="GND", +) + +# Define the port. + +edb.padstacks.set_solderball(con_pin, "Virt_GND", isTopPlaced=False, ballDiam=0.1e-3) +port_name = edb.padstacks.create_coax_port(con_pin) + +# Display the model using the ``Edb.nets.plot()`` method. + +edb.nets.plot() + +# The EDB is complete. Now close the EDB and import it into HFSS as a "Layout Component". + +edb.save_edb() +edb.close_edb() +print("EDB saved correctly to {}. You can import in AEDT.".format(aedb_path)) + +# ## 3D component in HFSS +# +# First create an instance of the ``pyaedt.Hfss`` class. If you set +# > ``non_graphical = False +# +# then AEDT user interface will be visible after the following cell is executed. +# It is now possible to monitor the progress in the UI as each of the following cells is executed. +# All commands can be run without the UI by changing the value of ``non_graphical``. + +h3d = ansys.aedt.core.Hfss( + projectname="Demo_3DComp", + designname="Linear_Array", + specified_version="2024.2", + new_desktop_session=True, + non_graphical=non_graphical, + close_on_exit=True, + solution_type="Terminal", +) + +# Set units to ``mm``. + +h3d.modeler.model_units = "mm" + +# ## Import the EDB as a 3D component +# +# One or more layout components can be imported into HFSS. +# The combination of layout data and 3D CAD data helps streamline model creation and setup. + +component = h3d.modeler.insert_layout_component(aedb_path, parameter_mapping=True) + +# ## Expose the component parameters +# +# If a layout component is parametric, you can expose and change parameters in HFSS + +# + +component.parameters + +w1_name = "{}_{}".format("w1", h3d.modeler.user_defined_component_names[0]) +h3d[w1_name] = 0.0015 +# - + +# ### Radiation Boundary Assignment +# +# The 3D domain includes the air volume surrounding the antenna. +# This antenna will be simulted from 20 GHz - 50 GHz. +# +# A "radiation boundary" will be assigned to the outer boundaries of the domain. +# This boundary should be roughly one quarter wavelength away from the radiating structure: +# +# $$ \lambda/4 = \frac{c_0}{4 f} \approx 2.8mm $$ + +# + +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") +# - + +# ### Set up analysis +# +# The finite element mesh is adapted iteratively. +# The maximum number of adaptive passes is set using the ``MaximumPasses`` property. +# This model converges such that the $S_{11}$ is independent of the mesh. +# The default accuracy setting is: +# $$ \max(|\Delta S|) < 0.02 $$ + +setup = h3d.create_setup() +setup.props["Frequency"] = "20GHz" +setup.props["MaximumPasses"] = 10 + +# Specify properties of the frequency sweep: + +sweep1 = setup.add_sweep(sweepname="20GHz_to_50GHz") +sweep1.props["RangeStart"] = "20GHz" +sweep1.props["RangeEnd"] = "50GHz" +sweep1.update() + +# Solve the project + +h3d.analyze() + +# ## Plot results outside AEDT +# +# Plot results using Matplotlib. + +trace = h3d.get_traces_for_plot() +solution = h3d.post.get_solution_data(trace[0]) +solution.plot() + +# ## Plot far fields in AEDT +# +# Plot radiation patterns in AEDT. + +# + +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") +# - + +# ## Plot far fields in AEDT +# +# Plot radiation patterns in AEDT + +new_report.report_type = "3D Polar Plot" +new_report.secondary_sweep = "Phi" +new_report.create("Realized3D") + +# ## Plot far fields outside AEDT +# +# Plot radiation patterns outside AEDT + +solutions_custom = new_report.get_solution_data() +solutions_custom.plot_3d() + +# ## Plot E Field on nets and layers +# +# Plot E Field on nets and layers in AEDT + +h3d.post.create_fieldplot_layers_nets( + [["TOP", "Array_antenna"]], + "Mag_E", + intrinsics={"Freq": "20GHz", "Phase": "0deg"}, + plot_name="E_Layers", +) + +# ## Close AEDT +# +# After the simulation completes, the application can be released from the +# :func:`ansys.aedt.core.Desktop.release_desktop` method. +# All methods provide for saving the project before closing AEDT. + +h3d.save_project(os.path.join(temp_dir.name, "test_layout.aedt")) +h3d.release_desktop() + +# ### Clean up the temporary directory +# +# The following command removes the project and the temporary directory. +# If you'd like to save this project, save it to a folder of your choice prior +# to running the following cell. + +temp_dir.cleanup() diff --git a/examples/high_frequency/antenna/_static/patch_edb.png b/examples/high_frequency/antenna/_static/patch_edb.png new file mode 100644 index 000000000..a6e31fe73 Binary files /dev/null and b/examples/high_frequency/antenna/_static/patch_edb.png differ diff --git a/examples/high_frequency/antenna/dipole.py b/examples/high_frequency/antenna/dipole.py index a30f1da00..bf9b7f717 100644 --- a/examples/high_frequency/antenna/dipole.py +++ b/examples/high_frequency/antenna/dipole.py @@ -69,24 +69,11 @@ hfss.create_open_region(frequency="1GHz") # ## Create setup -# -# Create a setup with a sweep to run the simulation. setup = hfss.create_setup("MySetup") setup.props["Frequency"] = "1GHz" setup.props["MaximumPasses"] = 1 -hfss.create_linear_count_sweep( - setup=setup.name, - units="GHz", - start_frequency=0.5, - stop_frequency=1.5, - num_of_freq_points=101, - name="sweep1", - sweep_type="Interpolating", - interpolation_tol=3, - interpolation_max_solutions=255, - save_fields=False, -) + # ## Run simulation @@ -110,22 +97,26 @@ report_category="Far Fields", ) -# Create a far fields report using the ``report_by_category.far field()`` method. +# Create a far field report. new_report = hfss.post.reports_by_category.far_field( "db(RealizedGainTotal)", hfss.nominal_adaptive, "3D" ) -new_report.variations = variations -new_report.primary_sweep = "Theta" -new_report.create("Realized2D") - -# Generate multiple plots using the ``new_report`` object. This code generates -# 2D and 3D polar plots. - new_report.report_type = "3D Polar Plot" new_report.secondary_sweep = "Phi" +new_report.variations["Freq"] = ["1000MHz"] new_report.create("Realized3D") +# This code generates a 2D plot. + +hfss.field_setups[2].phi_step = 90 +new_report2 = hfss.post.reports_by_category.far_field( + "db(RealizedGainTotal)", hfss.nominal_adaptive, hfss.field_setups[2].name +) +new_report2.variations = variations +new_report2.primary_sweep = "Theta" +new_report2.create("Realized2D") + # Get solution data using the ``new_report`` object and postprocess or plot the # data outside AEDT. @@ -151,6 +142,7 @@ ) new_report.primary_sweep = "Theta" new_report.far_field_sphere = "3D" +new_report.variations["Freq"] = ["1000MHz"] solutions = new_report.get_solution_data() # Generate a 3D plot using Matplotlib. @@ -177,7 +169,7 @@ ffdata = hfss.get_antenna_data( sphere="Sphere_Custom", setup=hfss.nominal_adaptive, - frequencies=["1000MHz"], + frequencies=1 ) # ## Generate 2D cutout plot diff --git a/examples/high_frequency/antenna/index.rst b/examples/high_frequency/antenna/index.rst index 779ff1df1..2f8390c00 100644 --- a/examples/high_frequency/antenna/index.rst +++ b/examples/high_frequency/antenna/index.rst @@ -56,6 +56,19 @@ These examples use PyAEDT to show some antenna applications. This example shows how to use PyAEDT to model and simulate a unit cell for a frequency-selective surface in HFSS. + .. grid-item-card:: Planar Antenna with EDB + :padding: 2 2 2 2 + :link: 5G_antenna_parametrics + :link-type: doc + + .. image:: _static/patch_edb.png + :alt: FSS + :width: 250px + :height: 200px + :align: center + + This example shows how to use PyAEDT to model and simulate a unit cell for a frequency-selective surface in HFSS. + .. grid-item-card:: Circuit schematic creation and analysis :padding: 2 2 2 2 :link: ../../aedt_general/modeler/circuit_schematic @@ -105,3 +118,4 @@ These examples use PyAEDT to show some antenna applications. ../../aedt_general/modeler/circuit_schematic large_scenarios/index interferences/index + 5G_antenna_parametrics diff --git a/examples/high_frequency/layout/index.rst b/examples/high_frequency/layout/index.rst index 559934584..0552aa197 100644 --- a/examples/high_frequency/layout/index.rst +++ b/examples/high_frequency/layout/index.rst @@ -6,32 +6,6 @@ These examples use PyAEDT to show some layout applications. .. grid:: 2 - .. grid-item-card:: Layout preprocessing - :padding: 2 2 2 2 - :link: https://edb.docs.pyansys.com/version/stable/examples/legacy_standalone/index.html - :link-type: url - - .. image:: _static/pyedb.png - :alt: PyEDB - :width: 250px - :height: 200px - :align: center - - Links to examples in the AEDT documentation that show how to use the legacy PyEDB API as a standalone package. - - .. grid-item-card:: PyEDB configuration files - :padding: 2 2 2 2 - :link: https://edb.docs.pyansys.com/version/stable/examples/use_configuration/index.html - :link-type: url - - .. image:: _static/pyedb2.png - :alt: PyEDB2 - :width: 250px - :height: 200px - :align: center - - Links to examples in the PyAEDT documentation that show how to use PyEDB configuration files. - .. grid-item-card:: Power integrity :padding: 2 2 2 2 :link: power_integrity/index @@ -58,18 +32,18 @@ These examples use PyAEDT to show some layout applications. Provides signal integrity examples - .. grid-item-card:: HFSS 3D Layout GUI modificatation + .. grid-item-card:: PyEDB workflows examples :padding: 2 2 2 2 - :link: gui_manipulation + :link: workflows/index :link-type: doc - .. image:: _static/user_interface.png - :alt: UI 3D Layout + .. image:: _static/pyedb2.png + :alt: PyEDB2 :width: 250px :height: 200px :align: center - Provides HFSS 3D Layout GUI modification examples. + PyEDB related examples to worklows and file management. .. toctree:: @@ -77,4 +51,4 @@ These examples use PyAEDT to show some layout applications. power_integrity/index signal_integrity/index - gui_manipulation \ No newline at end of file + workflows/index diff --git a/examples/high_frequency/layout/power_integrity/index.rst b/examples/high_frequency/layout/power_integrity/index.rst index c24e5b8d2..8c044a47d 100644 --- a/examples/high_frequency/layout/power_integrity/index.rst +++ b/examples/high_frequency/layout/power_integrity/index.rst @@ -18,7 +18,35 @@ These examples use PyAEDT to show power integrity examples. This example shows how to use the Ansys Electronics Database (EDB) for power integrity analysis. - .. grid-item-card:: DC IR analysis + + .. grid-item-card:: Via Array + :padding: 2 2 2 2 + :link: ../../../00_edb/legacy_standalone/differential_vias + :link-type: doc + + .. image:: ../../../00_edb/legacy_standalone/_static/diff_via.png + :alt: Differential Vias + :width: 250px + :height: 200px + :align: center + + This example shows how you can use EDB to create a layout. + + .. grid-item-card:: SIwave DC-IR Analysis + :padding: 2 2 2 2 + :link: ../../../00_edb/legacy_standalone/01_edb_example + :link-type: doc + + .. image:: ../../../00_edb/legacy_standalone/_static/siwave_dcir.png + :alt: SIwave DCIR + :width: 250px + :height: 200px + :align: center + + This example demonstrates the use of EDB to interact with a PCB layout and run DC-IR analysis in SIwave. + + + .. grid-item-card:: SIwave DC IR analysis (HFSS 3D Layout) :padding: 2 2 2 2 :link: dcir :link-type: doc @@ -32,7 +60,7 @@ These examples use PyAEDT to show power integrity examples. This example shows how to configure EDB for DC IR analysis and load EDB into the HFSS 3D Layout UI for analysis and postprocessing. - .. grid-item-card:: PCB DCIR analysis + .. grid-item-card:: Q3D DCIR analysis :padding: 2 2 2 2 :link: dcir_q3d :link-type: doc @@ -110,3 +138,5 @@ These examples use PyAEDT to show power integrity examples. ../../../aedt_general/modeler/circuit_schematic ../../../aedt_general/modeler/netlist_to_schematic ../../../aedt_general/report/touchstone_file + ../../../00_edb/legacy_standalone/differential_vias + ../../../00_edb/legacy_standalone/01_edb_example \ No newline at end of file diff --git a/examples/high_frequency/layout/signal_integrity/_static/connector_example.png b/examples/high_frequency/layout/signal_integrity/_static/connector_example.png new file mode 100644 index 000000000..a1083574c Binary files /dev/null and b/examples/high_frequency/layout/signal_integrity/_static/connector_example.png differ diff --git a/examples/high_frequency/layout/signal_integrity/_static/edb_example_12_sma_connector_on_board.png b/examples/high_frequency/layout/signal_integrity/_static/edb_example_12_sma_connector_on_board.png new file mode 100644 index 000000000..1226d2420 Binary files /dev/null and b/examples/high_frequency/layout/signal_integrity/_static/edb_example_12_sma_connector_on_board.png differ diff --git a/examples/high_frequency/layout/signal_integrity/_static/parametrized_design.png b/examples/high_frequency/layout/signal_integrity/_static/parametrized_design.png new file mode 100644 index 000000000..80bcb84b1 Binary files /dev/null and b/examples/high_frequency/layout/signal_integrity/_static/parametrized_design.png differ diff --git a/examples/high_frequency/layout/signal_integrity/_static/parametrized_edb.png b/examples/high_frequency/layout/signal_integrity/_static/parametrized_edb.png new file mode 100644 index 000000000..5f708e72c Binary files /dev/null and b/examples/high_frequency/layout/signal_integrity/_static/parametrized_edb.png differ diff --git a/examples/high_frequency/layout/signal_integrity/_static/pcb_transition_parameterized.png b/examples/high_frequency/layout/signal_integrity/_static/pcb_transition_parameterized.png new file mode 100644 index 000000000..f75d4a2e6 Binary files /dev/null and b/examples/high_frequency/layout/signal_integrity/_static/pcb_transition_parameterized.png differ diff --git a/examples/high_frequency/layout/signal_integrity/index.rst b/examples/high_frequency/layout/signal_integrity/index.rst index be6e40795..97634684f 100644 --- a/examples/high_frequency/layout/signal_integrity/index.rst +++ b/examples/high_frequency/layout/signal_integrity/index.rst @@ -32,6 +32,20 @@ These examples use PyAEDT to show signal integrity examples. This example shows how to create a parameterized layout design and load the layout into HFSS 3D Layout for analysis and postprocessing. + .. grid-item-card:: Siwave differential pairs in Hfss 3D Layout + :padding: 2 2 2 2 + :link: serdes_differential + :link-type: doc + + .. image:: _static/parametrized_edb.png + :alt: Parametrized differential pairs + :width: 250px + :height: 200px + :align: center + + This example shows how to use PyAEDT to set up SYZ analysis on a + serdes channel. + .. grid-item-card:: Pre-layout Parameterized PCB :padding: 2 2 2 2 :link: pre_layout_parametrized @@ -158,7 +172,9 @@ These examples use PyAEDT to show signal integrity examples. pre_layout_parametrized ami multizone + serdes_differential circuit_transient + ../../../aedt_general/modeler/circuit_schematic ../../../aedt_general/modeler/netlist_to_schematic ../../emc/subcircuit diff --git a/examples/high_frequency/layout/signal_integrity/pre_layout.py b/examples/high_frequency/layout/signal_integrity/pre_layout.py index 5c961675c..6fdcdfda9 100644 --- a/examples/high_frequency/layout/signal_integrity/pre_layout.py +++ b/examples/high_frequency/layout/signal_integrity/pre_layout.py @@ -337,6 +337,28 @@ ) comp.angle = "90deg" +# ## Run simulation + +h3d.analyze(num_cores=4) + +# ## Visualize the return loss. + +h3d.post.create_report("dB(S(port_1, port_1))") + +# - + +# ## Create Field Plot on clip plane + + +# solutions = h3d.get_touchstone_data()[0] +# solutions.log_x = False +# solutions.plot() +cp_name = h3d.modeler.clip_plane() + +plot = h3d.post.create_fieldplot_cutplane( + cp_name, "Mag_E", h3d.nominal_adaptive, intrinsics={"Freq": "5GHz", "Phase": "0deg"} +) + # ## Release AEDT h3d.save_project() diff --git a/examples/high_frequency/layout/signal_integrity/serdes_differential.py b/examples/high_frequency/layout/signal_integrity/serdes_differential.py new file mode 100644 index 000000000..90458a4d0 --- /dev/null +++ b/examples/high_frequency/layout/signal_integrity/serdes_differential.py @@ -0,0 +1,230 @@ +# # EDB: Pin to Pin project +# +# This example demonstrates the use of the Electronics +# Database (EDB) interface to create a layout using the BOM and +# a configuration file. + +# ## Perform required imports +# +# The ``Hfss3dlayout`` class provides an interface to +# the 3D Layout editor in AEDT. +# on version 2023 R2. + +# + +import os +import tempfile + +import ansys.aedt.core + +import pyedb +from pyedb.misc.downloads import download_file + +# - + +# Download the AEDB file and copy it to a temporary folder. + +temp_dir = tempfile.TemporaryDirectory(suffix=".ansys") +target_aedb = download_file("edb/ANSYS-HSD_V1.aedb", destination=temp_dir.name) +print("Project folder is", target_aedb) + +# ## Launch EDB +# +# Launch the ``pyedb.Edb`` class using EDB 2023 R2. Length units are SI. + +# + +# Select EDB version (change it manually if needed, e.g. "2024.2") +edb_version = "2024.2" +print(f"EDB version: {edb_version}") + +edbapp = pyedb.Edb(target_aedb, edbversion=edb_version) +# - + +# ## Import definitions +# +# The definition file uses the [json](https://www.json.org/json-en.html) to +# map layout part numbers to their corresponding models. +# +# The model may be an RLC, S-parameter, or +# [SPICE](https://en.wikipedia.org/wiki/SPICE) model definition. +# Once imported, the definition is applied to the components in the layout. +# In this example, the JSON file is in the ``*.aedb`` folder and has the following format: +# ``` json +# { +# "SParameterModel": { +# "GRM32_DC0V_25degC_series": "./GRM32_DC0V_25degC_series.s2p" +# }, +# "SPICEModel": { +# "GRM32_DC0V_25degC": "./GRM32_DC0V_25degC.mod" +# }, +# "Definitions": { +# "CAPC1005X05N": { +# "Component_type": "Capacitor", +# "Model_type": "RLC", +# "Res": 1, +# "Ind": 2, +# "Cap": 3, +# "Is_parallel": false +# }, +# "'CAPC3216X180X55ML20T25": { +# "Component_type": "Capacitor", +# "Model_type": "SParameterModel", +# "Model_name": "GRM32_DC0V_25degC_series" +# }, +# "'CAPC3216X180X20ML20": { +# "Component_type": "Capacitor", +# "Model_type": "SPICEModel", +# "Model_name": "GRM32_DC0V_25degC" +# } +# } +# } +# ``` +# +# The ``Edb.components.import_definitions()`` method imports the component definitions that map +# electrical models to the components in the simulation model. + +edbapp.components.import_definition(os.path.join(target_aedb, "1_comp_definition.json")) + +# ## Import BOM +# +# The bill of materials (BOM) file provides the list of all components +# by reference designator, part name, component type, and nominal value. +# +# Components that are not contained in the BOM are deactivated in the +# simulation model. +# This example saves the CSV file in the ``aedb`` folder. +# +# ``` +# +------------+-----------------------+-----------+------------+ +# | RefDes | Part name | Type | Value | +# +============+=======================+===========+============+ +# | C380 | CAPC1005X55X25LL05T10 | Capacitor | 11nF | +# +------------+-----------------------+-----------+------------+ +# ``` +# +# Having red the information in the BOM and definitions file, electrical models can be +# assigned to all of the components in the simulation model. + +edbapp.components.import_bom( + os.path.join(target_aedb, "0_bom.csv"), + refdes_col=0, + part_name_col=1, + comp_type_col=2, + value_col=3, +) + +# ## Verify a Component +# +# Component property allows to access all components instances and their property with +# getters and setters. + +comp = edbapp.components["C1"] +comp.model_type, comp.value + +# ## Check component definition +# +# When an s-parameter model is associated to a component it will be available in +# nport_comp_definition property. + +edbapp.components.nport_comp_definition +edbapp.save_edb() + +# ## Configure the simulation setup +# +# This step enables the following: +# +# - Definition of the nets to include in the cutout region +# - Cutout details +# - Components to create the ports on +# - Simulation settings +# +# The ``Edb.new_simulaton_configuration()`` method returns an instance +# of the ``SimulationConfiguration`` class. + +# + +sim_setup = edbapp.new_simulation_configuration() +sim_setup.solver_type = sim_setup.SOLVER_TYPE.SiwaveSYZ +sim_setup.batch_solve_settings.cutout_subdesign_expansion = 0.003 +sim_setup.batch_solve_settings.do_cutout_subdesign = True +sim_setup.batch_solve_settings.use_pyaedt_cutout = True +sim_setup.ac_settings.max_arc_points = 6 +sim_setup.ac_settings.max_num_passes = 5 + +sim_setup.batch_solve_settings.signal_nets = [ + "PCIe_Gen4_TX2_CAP_P", + "PCIe_Gen4_TX2_CAP_N", + "PCIe_Gen4_TX2_P", + "PCIe_Gen4_TX2_N", +] +sim_setup.batch_solve_settings.components = ["U1", "X1"] +sim_setup.batch_solve_settings.power_nets = ["GND", "GND_DP"] +sim_setup.ac_settings.start_freq = "100Hz" +sim_setup.ac_settings.stop_freq = "6GHz" +sim_setup.ac_settings.step_freq = "10MHz" +# - + +# ## Implement the setup +# +# The cutout and all other simulation settings are applied to the simulation model. + +sim_setup.export_json(os.path.join(temp_dir.name, "configuration.json")) +edbapp.build_simulation_project(sim_setup) + +# ## Display the cutout +# +# Plot cutout once finished. The model is ready to simulate. + +edbapp.nets.plot(None, None) + +# ## Save and close EDB +# +# EDB is saved and re-opened in HFSS +# 3D Layout, where the HFSS simulation can be run. + +edbapp.save_edb() +edbapp.close_edb() + +# ## Open Electronics Desktop +# +# The EDB is opened in AEDT Hfss3DLayout. +# +# Set ``non_graphical=True`` to run the simulation in non-graphical mode. + +aedt_version = edb_version + +h3d = ansys.aedt.core.Hfss3dLayout( + specified_version=aedt_version, + projectname=target_aedb, + non_graphical=False, + new_desktop_session=False, +) + +# ## Analyze +# +# This project is ready to solve. +# Executing the following cell runs the HFSS simulation on the layout. + +h3d.analyze() + +# ## View results +# +# S-parameter data is loaded at the end of simulation. + +solutions = h3d.post.get_solution_data() + +# ## Plot results +# +# Plot S-Parameter data. + +solutions.plot(solutions.expressions, "db20") + +# ## Save and close AEDT +# +# HFSS 3D Layout is saved and closed. + +h3d.save_project() +h3d.release_desktop() + +# Clean up the temporary directory. All files and the temporary project +# folder will be deleted in the next step. + +temp_dir.cleanup() diff --git a/examples/high_frequency/layout/workflows/index.rst b/examples/high_frequency/layout/workflows/index.rst new file mode 100644 index 000000000..696bf37f3 --- /dev/null +++ b/examples/high_frequency/layout/workflows/index.rst @@ -0,0 +1,80 @@ +Workflows +~~~~~~~~~ + +These examples create end-to-end workflows with PyAEDT and PyEDB. + +.. grid:: 2 + + .. grid-item-card:: Configuration Files + :padding: 2 2 2 2 + :link: ../../../00_edb/use_configuration/index + :link-type: doc + + .. image:: ../../../00_edb/use_configuration/_static/configurator_2.png + :alt: PyEDB2 + :width: 250px + :height: 200px + :align: center + + Links to examples in the PyAEDT documentation that show how to use PyEDB configuration files. + + .. grid-item-card:: IPC2581 + :padding: 2 2 2 2 + :link: ../../../00_edb/legacy_standalone/edb_to_ipc2581 + :link-type: doc + + .. image:: ../../../00_edb/legacy_standalone/_static/ipc.png + :alt: PyEDB2 + :width: 250px + :height: 200px + :align: center + + This example shows how you can use PyAEDT to export an IPC2581 file. + + .. grid-item-card:: Plot Layout + :padding: 2 2 2 2 + :link: ../../../00_edb/legacy_standalone/Plot_nets + :link-type: doc + + .. image:: ../../../00_edb/legacy_standalone/_static/plot_nets.png + :alt: PyEDB2 + :width: 250px + :height: 200px + :align: center + + This example shows how to use the ``Edb`` class to view nets, layers and via geometry directly in Python. + + .. grid-item-card:: IC Workflow using GDS + :padding: 2 2 2 2 + :link: ../../../00_edb/legacy_standalone/GDS_workflow + :link-type: doc + + .. image:: ../../../00_edb/legacy_standalone/_static/gds.png + :alt: GDS + :width: 250px + :height: 200px + :align: center + + This example shows how to use the ``Edb`` class to manage ``GDS`` files, import them and setup analysis. + + .. grid-item-card:: HFSS 3D Layout GUI modificatation + :padding: 2 2 2 2 + :link: ../gui_manipulation + :link-type: doc + + .. image:: ../_static/user_interface.png + :alt: UI 3D Layout + :width: 250px + :height: 200px + :align: center + + Provides HFSS 3D Layout GUI modification examples. + + .. toctree:: + :hidden: + + ../../../00_edb/use_configuration/index + ../../../00_edb/legacy_standalone/edb_to_ipc2581 + ../../../00_edb/legacy_standalone/Plot_nets + ../../../00_edb/legacy_standalone/GDS_workflow + ../gui_manipulation \ No newline at end of file diff --git a/examples/high_frequency/multiphysics/_static/oven.png b/examples/high_frequency/multiphysics/_static/oven.png new file mode 100644 index 000000000..22c18a504 Binary files /dev/null and b/examples/high_frequency/multiphysics/_static/oven.png differ diff --git a/examples/high_frequency/multiphysics/index.rst b/examples/high_frequency/multiphysics/index.rst index e010c0070..4ec0c273d 100644 --- a/examples/high_frequency/multiphysics/index.rst +++ b/examples/high_frequency/multiphysics/index.rst @@ -90,6 +90,21 @@ These examples use PyAEDT to show some multiphysics applications. This example shows how to create a two-way coupling between HFSS and Icepak. + .. grid-item-card:: HFSS-Icepak microwave oven analysis + :padding: 2 2 2 2 + :link: microwave_oven + :link-type: doc + + .. image:: _static/oven.png + :alt: HFSS Icepak + :width: 250px + :height: 200px + :align: center + + This example shows how to couple together HFSS and Icepak to run multiphysics + analysis on a well know problem of microwave oven. + + .. toctree:: :hidden: @@ -99,4 +114,5 @@ These examples use PyAEDT to show some multiphysics applications. ../../electrothermal/coaxial_hfss_icepak ../../electrothermal/electrothermal ../../electrothermal/icepak_circuit_hfss_coupling + oven diff --git a/examples/high_frequency/multiphysics/microwave_oven.py b/examples/high_frequency/multiphysics/microwave_oven.py new file mode 100644 index 000000000..3e0e363e3 --- /dev/null +++ b/examples/high_frequency/multiphysics/microwave_oven.py @@ -0,0 +1,372 @@ +# # Multi physics HFSS-Icepak microwave oven simulation +# This example shows how to couple together HFSS and Icepak to +# run multi physics analysis on a well-know problem of microwave oven. +# +# Keywords: **HFSS**, **modal**, **microwave oven**, **Icepak**, **Multi physics**. +# + +# + +# ## Prerequisites +# +# ### Perform imports + +import ansys.aedt.core +import os +import time +import tempfile +import pyvista +import numpy as np +from ansys.aedt.core import generate_unique_name +from ansys.aedt.core.visualization.plot.pdf import AnsysReport + +# ### Define constants. +# +# Constants help ensure consistency and avoid repetition throughout the example. + +AEDT_VERSION = "2024.2" +NUM_CORES = 4 +NG_MODE = False # Open AEDT UI when it is launched. + +# ### Create temporary directory +# +# Create a temporary working directory. +# The name of the working folder is stored in ``working_dir.name``. + +working_dir = tempfile.TemporaryDirectory(suffix=".ansys") + +# ### Download the project +# Download and open the project. Save it to the temporary working folder. + + +parasolid_path = ansys.aedt.core.downloads.download_file( + source="oven", name="gingerbread.x_t", destination=working_dir.name +) +oven_path = ansys.aedt.core.downloads.download_file( + source="oven", name="microwave_oven.aedt", destination=working_dir.name +) + + +# ### Launch HFSS +# Open AEDT and initialize the microwave oven project. +# +# After the project is opened, we save it in our working directory. + +hfss = ansys.aedt.core.Hfss(version=AEDT_VERSION, + project=oven_path, + non_graphical=NG_MODE, + new_desktop=True) +hfss.save_project(file_name=os.path.join(working_dir.name,'lets_cook.aedt')) + +# ## Model Preparation +# +# ### Assign material +# This phase is fundamental because we need to assign correct material properties that are valid for both electrical and thermal analysis. +# +# PyAEDT simplifies the creation and modification of a material +# definitions using _getter_ and _setter_ methods. In this example we modify 5 material parameters. + +ginger_material = hfss.materials.add_material(name="ginger_bread") +ginger_material.permittivity = 41 +ginger_material.dielectric_loss_tangent = 0.32 +ginger_material.thermal_conductivity = 0.38 +ginger_material.mass_density = 1831 +ginger_material.specific_heat = 3520 + +# ### Import the gingerbread man and assign material +# Once the object is imported all of its properties can be edited. +# We are gonna move the gingerbread at the center of the plate and assign material to it. +# +# Finally, we are gonna change the transparency of the glass bowl. + +hfss.modeler.import_3d_cad(input_file=parasolid_path) +ginger_bread = hfss.modeler["plateauPainEpices_Unnamed_5"] +hfss.modeler.move(assignment=ginger_bread, vector=["-0.5in", "-0.2in","-38.1mm"]) +ginger_bread.material_name=ginger_material.name +hfss.modeler["glassBowl"].transparency = 0.75 + +# ### Export an image +# At the end of this example we will generate a PDF report that summarizes the workflow and simulation results. +# +# We now save an image of the model as a PNG file to insert into the report later. + +hfss.post.export_model_picture(full_name=os.path.join(working_dir.name,'ginger_bread_cookie.png')) + +# ### Launch Icepak +# In order to run a multiphysics analysis we need to create an Icepak project that will be retrieve the loss data from HFSS to use as a heat source. + + +ipk = ansys.aedt.core.Icepak(solution_type="Transient Thermal") +ipk.copy_solid_bodies_from(design=hfss, no_vacuum=False, no_pec=False, include_sheets=True) +ipk.modeler["Region"].delete() +ipk.modeler.fit_all() +exc = ipk.assign_em_losses( + design=hfss.design_name, + setup=hfss.setups[0].name, + sweep="LastAdaptive", + map_frequency=hfss.setups[0].props["Frequency"], + surface_objects=[], + assignment=["glassBowl", ginger_bread.name] +) + +# ### Thermal boundaries +# Main thermal boundaries will be free opening of the microwave oven. +# +# In this example we set 2 different types of openings on the two faces of the oven. + +ipk.modeler["ovenCavity"].transparency = 1 +ipk.assign_free_opening(assignment=ipk.modeler["ovenCavity"].top_face_y.id, + flow_type="Velocity", + velocity=["0m_per_sec", "-0.5m_per_sec", "0m_per_sec"]) +ipk.assign_free_opening(assignment=ipk.modeler["ovenCavity"].bottom_face_y.id, + flow_type="Pressure") + + +# #### Icepak multiple reference frame (MRF) +# The MRF assumes mesh rotation as a solid block. In this example is useful to rotate the oven plate and cookie to reduce cooking time. + +rot_cyl= ipk.modeler.create_cylinder(orientation="Z", + origin=[158.75 ,228.6 ,0], + radius=110, height=150, + material="air", + name="rotating_cylinder") +rot_cyl.display_wireframe=True +rot_cyl.transparency = 1 +block = ipk.assign_solid_block(rot_cyl.name,0) +block.props["Use MRF"]=True +block.props["MRF"]="6rpm" +block.props["Is Cylinder MRF"]=True + +# ### Icepak mesh settings +# Icepak mesh settings are used to optimize the simulation time and accuracy. + +ipk.mesh.global_mesh_region.manual_settings = True +ipk.mesh.global_mesh_region.settings["MaxElementSizeX"] = "15mm" +ipk.mesh.global_mesh_region.settings["MaxElementSizeY"] = "15mm" +ipk.mesh.global_mesh_region.settings["MaxElementSizeZ"] = "15mm" +ipk.mesh.global_mesh_region.settings["BufferLayers"]='2' +ipk.mesh.global_mesh_region.settings["MaxLevels"]='2' +ipk.mesh.global_mesh_region.update() + +# ### Icepak solution setup +# In this example we are limiting the number of steps to a maximum of 5 steps to make the example quick to run. Ideally this number has to be increased to improve the simulation accuracy and obtain more precise results. + +setup = ipk.create_setup() +setup.props["SaveFieldsType"] = "Every N Steps" +setup.props["N Steps"] = "5" +setup.props["Turbulent Model Eqn"] = "kOmegaSST" +setup.props["Flow Regime"] = "Turbulent" +solved = False +stop_time = 0 + +# ### Icepak report preparation + +# + +ipk.save_project() +ginger_bread_thermal = ipk.modeler["plateauPainEpices_Unnamed_5"] +ginger_bread_thermal.transparency = 1 +objects = ipk.modeler.non_model_objects[::] + ["glassBowl"] + +microwave_objects = ipk.post.export_model_obj(objects, export_as_multiple_objects=True, ) +# - + +# ### Initialize Ansys report +# +# ``AnsysReport`` PyAEDT class that allows creation of +# simple and effective PDF reports +# that include text, images, tables and charts. + +# + +report = AnsysReport( + version=ipk.aedt_version_id, design_name=ipk.design_name, project_name=ipk.project_name + ) +report.create() +report.add_section() +report.add_chapter(f"Ansys GingerBread Recipe") +report.add_sub_chapter("Ingredients") + +report.add_text("Step 1: Melt the sugar, golden syrup and butter in a saucepan, then bubble for 1-2 mins.") +report.add_text("Leave to cool for about 10 mins.") + +report.add_text("Step 2: Tip the flour, baking soda and spices into a large bowl.") +report.add_text("Add the warm syrup mixture and the egg, stir everything together, then gently knead in the bowl until smooth and streak-free.") +report.add_text("The dough will firm up once cooled. Wrap in cling film and chill for at least 30 mins.") + +report.add_text("Step 3: Remove the dough from the fridge, leave at room temperature until softened. ") +report.add_text("Set the microwave oven power to 1200W but be careful about the time!!!") +report.add_page_break() +report.add_sub_chapter("Design the Microwave Oven... with HFSS") +report.add_text("An accurate Microwave Oven design requires:") +report.add_text("1- Ansys HFSS") +report.add_text("2- PyAEDT") + +report.add_image(path=os.path.join(working_dir.name,'ginger_bread_cookie.png'), + caption="HFSS Design of Ansys Microwave Oven") + +report.add_page_break() +report.add_sub_chapter("Determine cooking time... with Icepak") +report.add_text("") +report.add_text("") +report.add_text("A complete Icepak project requires:") +report.add_text("1- Correct material assignment") +report.add_text("2- Rotating gingerbread") +report.add_text("3- Convection settings") +report.add_text("4- Accurate EM Losses from HFSS") +report.add_page_break() +report.add_sub_chapter("Recipe experiments") + + +# - + +# ### Compute average temperature of the cookie +# The following set of commands show how to use Icepak field summary to +# compute the temperature on the gingerbread biscuit and get the mean value of it. + +def get_average_temperature(): + fs = ipk.post.create_field_summary() + fs.add_calculation(entity="Object", + geometry="Surface", + geometry_name=ginger_bread_thermal.name, + quantity="Temperature", + time=f"{stop_time}s") + df = fs.get_field_summary_data(pandas_output=True) + return float(df["Mean"]) + +# ### Method to generate streamline plot on gingerbread cookie +# This method encapsulate a set of action to plot and arrange the view of +# the gingerbread inside the microwave oven with velocity streamline plot. +# The view is set to front $(y-z)$. + +def generate_streamline(stop): + def generate_case(quantity_in, field_time, assignment=["ovenCavity", "rotating_cylinder"]): + f1 = ipk.post.create_fieldplot_volume(assignment=assignment, + quantity=quantity_in, + intrinsics={"Time": f"{field_time}s"}) + air_ux_case = ipk.post.export_field_plot(plot_name=f1.name, + output_dir=working_dir.name, + file_format="case") + mesh_in = pyvista.read(air_ux_case) + return mesh_in + + mesh_ux = generate_case(quantity_in="Ux", field_time=stop) + mesh_uy = generate_case(quantity_in="Uy", field_time=stop) + mesh_uz = generate_case(quantity_in="Uz", field_time=stop) + mesh_temp = generate_case(quantity_in="Temperature", field_time=stop) + mesh_ginger = generate_case(assignment=["plateauPainEpices_Unnamed_5"], + quantity_in="Temperature", + field_time=stop) + + temp_block = mesh_temp[0] # First block for temperature + # Extract the actual mesh (assuming it's the first block in MultiBlock) + ux_block = mesh_ux[0] # First block + uy_block = mesh_uy[0] # First block + uz_block = mesh_uz[0] # First block + ux = ux_block.point_data.values()[0] + uy = uy_block.point_data.values()[0] + uz = uz_block.point_data.values()[0] + temperature = temp_block.point_data.values()[0] # Temperature data + velocity = np.column_stack((ux, uy, uz)) + ux_block.point_data["Velocity"] = velocity + ux_block.point_data["Temperature"] = temperature + seed = pyvista.Box(bounds=(50, 260, 0, 500, -1, 150), level=10) + streamlines = ux_block.streamlines_from_source( + seed, max_time=200.0, integration_direction="both" + ) + + pl = pyvista.Plotter(off_screen=True, window_size=(3840, 2160)) + for filename, color, opacity in microwave_objects: + reader = pyvista.get_reader(filename) + mesh = reader.read() + pl.add_mesh( + mesh, + smooth_shading=True, + split_sharp_edges=True, + color=color, + opacity=opacity, + ambient=0.05, + ) + + pl.add_mesh(streamlines, line_width=4, scalars="Temperature", cmap="coolwarm") + pl.add_mesh(mesh_ginger) + pl.enable_ssao(kernel_size=128, radius=15, bias=0.5) + pl.enable_anti_aliasing("ssaa") + pl.camera_position = "yz" + pl.camera.elevation += 20 + pl.enable_ssao(kernel_size=128, radius=15, bias=0.5) + pl.enable_anti_aliasing("ssaa") + pl.screenshot(os.path.join(working_dir.name, "streamlines.png")) + return os.path.join(working_dir.name, "streamlines.png") + + +# ### Method to generate temperature plot on gingerbread +# This method encapsulates a set of actions to plot and arrange the view of +# the gingerbread inside the microwave oven. The view is set to front $(y-z)$. + + +def generate_temps(stop): + pl = ipk.post.plot_field( + quantity="Temperature", + assignment=ginger_bread_thermal.faces, + plot_cad_objs=True, + show=False, + intrinsics={"Time": f"{stop}s"}, + scale_min=22, + scale_max=110, + show_legend=False, + dark_mode=False, + ) + for mw_obj in microwave_objects: + pl.add_object(mw_obj[0], mw_obj[1], mw_obj[2]) + pl.camera_position = 'yz' + pl.elevation_angle = 20 + pl.plot(export_image_path=os.path.join(working_dir.name, f'{generate_unique_name("Temperature")}.jpg'), + show=False) + return pl + + +# ## Cook the gingerbread +# ### Loop to determine transient time +# This is the core of our optimization process. We increase the Icepak stop time by steps of 5 seconds until the mean temperature of the gingerbread reaches the 50 degrees. We could also have used an optimizer (Optislang) or run a longer time and plot the average temperature over time. + +while not solved: + stop_time = stop_time + 5 + setup.props["Stop Time"] = f"{stop_time}s" + setup.analyze(cores=4, tasks=4) + mean_temperature = get_average_temperature() + if mean_temperature>50: + solved = True + report.add_page_break() + report.add_sub_chapter(f"The Gingerbread is ready!!!!") + report.add_text(f"The perfect time for cooking the Gingerbread is {stop_time}s") + plot4 = generate_temps(stop_time) + report.add_image(plot4.image_file, f"GingerBread at the end of cooking.") + else: + ipk.logger.info(f'Mean Temperature in the Gingerbread is {mean_temperature} after {stop_time}s in the microwave') + if mean_temperature > 30: + report.add_text(f"Gingerbread is almost ready. Don't worry we will notify you when ready.") + output_file = generate_streamline(stop_time) + report.add_image(os.path.join(working_dir.name, "streamlines.png"), f"GingerBread while cooking after {stop_time}s") + else: + report.add_text(f"Take a cup of tea and relax. It will take longer.") + ipk.save_project() + +# ### Generate PDF + +report.add_toc() +report.save_pdf(working_dir.name, "Gingerbread_ansys_recipe.pdf") + +# ## Release AEDT +# +# Release AEDT and close the example. + +ipk.save_project() +ipk.release_desktop() +# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory. +time.sleep(3) + +# ### 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. + +working_dir.cleanup() diff --git a/examples/high_frequency/multiphysics/mri.py b/examples/high_frequency/multiphysics/mri.py index 4561bbce3..beb4130c7 100644 --- a/examples/high_frequency/multiphysics/mri.py +++ b/examples/high_frequency/multiphysics/mri.py @@ -324,8 +324,8 @@ mesh_box.model = False mesh_region = ipk.mesh.assign_mesh_region([mesh_box.name]) mesh_region.UserSpecifiedSettings = False -mesh_region.Level = 4 mesh_region.update() +mesh_region.properties["Mesh Resolution"] = 4 # ## Create point monitor # diff --git a/examples/high_frequency/radiofrequency_mmwave/spiral.py b/examples/high_frequency/radiofrequency_mmwave/spiral.py index f1e4dc923..c46adc140 100644 --- a/examples/high_frequency/radiofrequency_mmwave/spiral.py +++ b/examples/high_frequency/radiofrequency_mmwave/spiral.py @@ -57,6 +57,7 @@ Nr = 10 gap = 3 hfss["Tsub"] = "6" + hfss.modeler.model_units +hfss["thickness"] = f"{thickness} {hfss.modeler.model_units}" # ## Standardize polyline # @@ -101,7 +102,7 @@ def create_line(pts): hfss.modeler.create_box( [x0 - width / 2, y0 - width / 2, -gap - thickness / 2], [width, width, gap + thickness], - matname="copper", + material="copper", ) # Create port 1. @@ -129,13 +130,13 @@ def create_line(pts): # + hfss.modeler.create_box( - [x1 - 20, x1 - 20, "-Tsub-{}{}/2".format(thickness, hfss.modeler.model_units)], + [x1 - 20, x1 - 20, "-Tsub-thickness/2"], [-2 * x1 + 40, -2 * x1 + 40, "Tsub"], material="silicon", ) hfss.modeler.create_box( - [x1 - 20, x1 - 20, "-Tsub-{}{}/2".format(thickness, hfss.modeler.model_units)], + [x1 - 20, x1 - 20, "-Tsub-thickness/2"], [-2 * x1 + 40, -2 * x1 + 40, -0.1], material="PEC", ) @@ -150,9 +151,7 @@ def create_line(pts): [ x1 - 20, x1 - 20, - "-Tsub-{}{}/2 - 0.1{}".format( - thickness, hfss.modeler.model_units, hfss.modeler.model_units - ), + "-Tsub-thickness/2 - 0.1{}".format(hfss.modeler.model_units), ], [-2 * x1 + 40, -2 * x1 + 40, 100], name="airbox", diff --git a/examples/low_frequency/multiphysics/maxwell_icepak.py b/examples/low_frequency/multiphysics/maxwell_icepak.py index a421dc07d..a131f5302 100644 --- a/examples/low_frequency/multiphysics/maxwell_icepak.py +++ b/examples/low_frequency/multiphysics/maxwell_icepak.py @@ -225,7 +225,6 @@ # - # ### Analytic calculation of DC resistance - # + cu_cond = float(cu_litz.conductivity.value) diff --git a/examples/low_frequency/team_problem/asymmetric_conductor.py b/examples/low_frequency/team_problem/asymmetric_conductor.py index aa86bd64d..b14437e91 100644 --- a/examples/low_frequency/team_problem/asymmetric_conductor.py +++ b/examples/low_frequency/team_problem/asymmetric_conductor.py @@ -7,10 +7,11 @@ # # Keywords: **Maxwell 3D**, **Asymmetric conductor**. -# ## Perform imports and define constants +# ## Prerequisites # -# Perform required imports. +# ### Perform imports +# + import os import tempfile import time @@ -18,28 +19,30 @@ import numpy as np from ansys.aedt.core import Maxwell3d from ansys.aedt.core.generic.general_methods import write_csv +# - -# Define constants. +# ### Define constants +# Constants help ensure consistency and avoid repetition throughout the example. AEDT_VERSION = "2024.2" NUM_CORES = 4 NG_MODE = False # Open AEDT UI when it is launched. -# ## Create temporary directory +# ### Create temporary directory +# +# Create a temporary working directory. +# The name of the working folder is stored in ``temp_folder.name``. # -# 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``. +# > **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. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") -# ## Launch AEDT and Maxwell 3D +# ### Launch Maxwell 3D # # Create an instance of the ``Maxwell3d`` class named ``m3d`` by providing # the project and design names, the solver, and the version. -# + project_name = os.path.join(temp_folder.name, "COMPUMAG2.aedt") m3d = Maxwell3d( project=project_name, @@ -50,9 +53,10 @@ new_desktop=True, ) m3d.modeler.model_units = "mm" -# - -# ## Add Maxwell 3D setup +# ## Model Preparation +# +# ### Define the Maxwell 3D analysis setup # # Add a Maxwell 3D setup with frequency points at DC, 50 Hz, and 200Hz. # Otherwise, the default PyAEDT setup values are used. To approximate a DC field in the @@ -77,7 +81,7 @@ setup.props["PercentError"] = 0.4 # - -# ## Define coil dimensions +# ### Define coil dimensions # # Define coil dimensions as shown on the TEAM7 drawing of the coil. @@ -89,6 +93,7 @@ coil_height = 100 coil_centre = [294 - 25 - 150 / 2, 25 + 150 / 2, 19 + 30 + 100 / 2] +# ### Define expressions to evaluate solution data # Use expressions to construct the three dimensions needed to describe the midpoints of # the coil. @@ -96,6 +101,7 @@ dim2 = coil_internal / 2 - coil_r1 dim3 = dim2 + np.sqrt(((coil_r1 + (coil_r2 - coil_r1) / 2) ** 2) / 2) +# ### Draw the coil # Use coordinates to draw a polyline along which to sweep the coil cross sections. P1 = [dim1, -dim2, 0] @@ -103,14 +109,12 @@ P3 = [dim3, dim3, 0] P4 = [dim2, dim1, 0] -# ## Create coordinate system for positioning coil +# Create a coordinate system to use as a reference for the coil. m3d.modeler.create_coordinate_system( origin=coil_centre, mode="view", view="XY", name="Coil_CS" ) -# ## Create polyline -# # Create a polyline. One quarter of the coil is modeled by sweeping a 2D sheet along a polyline. test = m3d.modeler.create_polyline( @@ -118,7 +122,7 @@ ) test.set_crosssection_properties(type="Rectangle", width=coil_thk, height=coil_height) -# ## Duplicate and unite polyline to create full coil +# Duplicate and unite the polyline to create the full coil. m3d.modeler.duplicate_around_axis( assignment="Coil", @@ -132,7 +136,7 @@ m3d.modeler.unite("Coil, Coil_3") m3d.modeler.fit_all() -# ## Assign material and if solution is allowed inside coil +# ### Assign material and enable the field solution inside the copper coil # # Assign the material ``Cooper`` from the Maxwell internal library to the coil and # allow a solution inside the coil. @@ -140,7 +144,7 @@ m3d.assign_material(assignment="Coil", material="Copper") m3d.solve_inside("Coil") -# ## Create terminal +# ### Create terminal # # Create a terminal for the coil from a cross-section that is split and one half deleted. @@ -148,25 +152,26 @@ m3d.modeler.separate_bodies(assignment="Coil_Section1") m3d.modeler.delete(assignment="Coil_Section1_Separate1") -# ## Add variable for coil excitation +# ### Add variable for coil excitation # -# Add a design variable for coil excitation. The NB units here are AmpereTurns. +# Use a parameter to define the coil current. +# The units in this case are Ampere$\times$Turns. Coil_Excitation = 2742 m3d["Coil_Excitation"] = str(Coil_Excitation) + "A" m3d.assign_current(assignment="Coil_Section1", amplitude="Coil_Excitation", solid=False) m3d.modeler.set_working_coordinate_system("Global") -# ## Add material +# ### Add material # # Add a material named ``team3_aluminium``. mat = m3d.materials.add_material("team7_aluminium") mat.conductivity = 3.526e7 -# ## Model aluminium plate with a hole +# ### Create the aluminium plate with a hole # -# Model the aluminium plate with a hole by subtracting two rectangular cuboids. +# Draw the aluminium plate with a hole by subtracting two cuboids. plate = m3d.modeler.create_box( origin=[0, 0, 0], sizes=[294, 294, 19], name="Plate", material="team7_aluminium" @@ -175,19 +180,17 @@ hole = m3d.modeler.create_box(origin=[18, 18, 0], sizes=[108, 108, 19], name="Hole") m3d.modeler.subtract(blank_list="Plate", tool_list=["Hole"], keep_originals=False) -# ## Draw background region +# ### Draw background region # -# Draw a background region that uses the default properties for an air region. +# The background air region defines the full volumetric solution domain. m3d.modeler.create_air_region( x_pos=100, y_pos=100, z_pos=100, x_neg=100, y_neg=100, z_neg=100 ) -# ## Adjust eddy effects for plate and coil +# ### Adjust eddy effects for plate and coil # -# Adjust the eddy effects for the plate and coil by turning off displacement currents -# for all parts. The setting for the eddy effect is ignored for the stranded conductor type -# used in the coil. +# Disable the eddy effects for the plate and coil. This forces the current to flow uniformly through the coils cross-section as would be the case for stranded wires. m3d.eddy_effects_on(assignment="Plate") m3d.eddy_effects_on( @@ -196,9 +199,9 @@ enable_displacement_current=False, ) -# ## Create expression for Z component of B in Gauss +# ### Create expression for $B_z$ in Gauss # -# Create an expression for the Z component of B in Gauss using PyAEDT advanced fields calculator. +# Create an expression for the $z$-component of $\bf{B}$ in Gauss using PyAEDT advanced fields calculator. bz = { "name": "Bz", @@ -221,7 +224,7 @@ } m3d.post.fields_calculator.add_expression(bz, None) -# ## Draw two lines along which to plot Bz +# ### Draw two lines along which to plot $B_z$ # # Draw two lines along which to plot Bz. The following code also adds a small cylinder # to refine the mesh locally around each line. @@ -454,22 +457,20 @@ ] # - -# ## Write dataset values to a CSV file +# ### Write dataset values to a CSV file # # Dataset details are used to encode test parameters in the text files. # For example, ``Bz A1_B1 050 0`` is the Z component of flux density ``B`` # along line ``A1_B1`` at 50 Hz and 0 deg. -# + line_length.insert(0, header[0]) for i in range(len(dataset)): data[i].insert(0, header[1]) ziplist = zip(line_length, data[i]) file_path = os.path.join(temp_folder.name, str(dataset[i]) + ".csv") write_csv(output_file=file_path, list_data=ziplist) -# - -# ## Create rectangular plots and import test data into report +# ### Create rectangular plots and import test data into report # # Create rectangular plots, using text file encoding to control their formatting. # Import test data into the correct plot and overlay with the simulation results. @@ -508,7 +509,7 @@ m3d.analyze(cores=NUM_CORES) -# ## Create plots of induced current and flux density on surface of plate +# ### Create plots of induced current and flux density on surface of plate # # Create two plots of the induced current (``Mag_J``) and the flux density (``Mag_B``) on the # surface of the plate. @@ -531,7 +532,9 @@ assignment=surf_list, quantity="Mesh", intrinsics=intrinsic_dict, plot_name="Mesh" ) -# ## Release AEDT +# ## Finish +# +# ### Save the project m3d.save_project() m3d.release_desktop() diff --git a/examples/low_frequency/team_problem/bath_plate.py b/examples/low_frequency/team_problem/bath_plate.py index 9e57d700d..25202e1e1 100644 --- a/examples/low_frequency/team_problem/bath_plate.py +++ b/examples/low_frequency/team_problem/bath_plate.py @@ -2,42 +2,46 @@ # # This example uses PyAEDT to set up the TEAM 3 bath plate problem and # solve it using the Maxwell 3D eddy current solver. -# # For more information on this problem, see this +# +# For more information on this problem, see this # [paper](https://www.compumag.org/wp/wp-content/uploads/2018/06/problem3.pdf). # # Keywords: **Maxwell 3D**, **TEAM 3 bath plate** -# ## Perform imports and define constants +# ## Prerequisites # -# Perform required imports. +# ### Perform imports +# + import os import tempfile import time import ansys.aedt.core +# - -# Define constants. +# ### Define constants +# Constants help ensure consistency and avoid repetition throughout the example. AEDT_VERSION = "2025.1" NUM_CORES = 4 NG_MODE = False # Open AEDT UI when it is launched. -# ## Create temporary directory +# ### Create temporary directory +# +# Create a temporary working directory. +# The name of the working folder is stored in ``temp_folder.name``. # -# 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``. +# > **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. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") -# ## Launch AEDT and Maxwell 3D +# ### Launch Maxwell 3D # # Create an instance of the ``Maxwell3d`` class named ``m3d`` by providing # the project and design names, the solver, and the version. -# + m3d = ansys.aedt.core.Maxwell3d( project=os.path.join(temp_folder.name, "COMPUMAG.aedt"), design="TEAM_3_Bath_Plate", @@ -47,9 +51,8 @@ new_desktop=True, ) uom = m3d.modeler.model_units = "mm" -# - -# ## Add variable +# ### Add variable # # Add a design variable named ``Coil_Position`` to use later to adjust the # position of the coil. @@ -57,14 +60,14 @@ Coil_Position = -20 m3d["Coil_Position"] = str(Coil_Position) + m3d.modeler.model_units -# ## Add material +# ### Add material # # Add a material named ``team3_aluminium`` for the ladder plate. mat = m3d.materials.add_material(name="team3_aluminium") mat.conductivity = 32780000 -# ## Draw background region +# ### Draw background region # # Draw a background region that uses the default properties for an air region. @@ -72,7 +75,7 @@ x_pos=100, y_pos=100, z_pos=100, x_neg=100, y_neg=100, z_neg=100 ) -# ## Draw ladder plate and assign material +# ### Draw ladder plate and assign material # # Draw a ladder plate and assign it the newly created material ``team3_aluminium``. @@ -90,7 +93,7 @@ keep_originals=False, ) -# ## Add mesh refinement to ladder plate +# ### Add mesh refinement to ladder plate # > **Note:** You can uncomment the following code. @@ -101,7 +104,7 @@ # name="Ladder_Mesh", # ) -# ## Draw search coil and assign excitation +# ### Draw search coil and assign excitation # # Draw a search coil and assign it a ``stranded`` current excitation. # The stranded type forces the current density to be constant in the coil. @@ -133,7 +136,7 @@ name="SearchCoil_Excitation", ) -# ## Draw a line for plotting Bz +# ### Draw a line for plotting $B_z$ # # Draw a line for plotting Bz later. Bz is the Z component of the flux # density. The following code also adds a small diameter cylinder to refine the @@ -144,7 +147,7 @@ poly = m3d.modeler.create_polyline(points=line_points, name="Line_AB_MeshRefinement") poly.set_crosssection_properties(type="Circle", width="0.5mm") -# ## Add Maxwell 3D setup +# ### Add Maxwell 3D setup # # Add a Maxwell 3D setup with frequency points at 50 Hz and 200 Hz. @@ -156,7 +159,7 @@ range_type="LinearStep", start=50, end=200, count=150, clear=True ) -# ## Adjust eddy effects +# ### Adjust eddy effects # # Adjust eddy effects for the ladder plate and the search coil. The setting for # eddy effect is ignored for the stranded conductor type used in the search coil. @@ -172,7 +175,7 @@ enable_displacement_current=True, ) -# ## Add linear parametric sweep +# ### Add linear parametric sweep # # Add a linear parametric sweep for the two coil positions. @@ -189,14 +192,16 @@ param["CopyMesh"] = False param["SolveWithCopiedMeshOnly"] = True -# ## Solve parametric sweep +# ### Solve parametric sweep # # Solve the parametric sweep directly so that results of all variations are available. m3d.save_project() param.analyze(cores=NUM_CORES) -# ## Create expression for Bz +# ## Postprocess +# +# ### Create expression for Bz # # Create an expression for Bz using PyAEDT advanced fields calculator. @@ -219,9 +224,9 @@ } m3d.post.fields_calculator.add_expression(bz, None) -# ## Plot mag(Bz) as a function of frequency +# ### Plot $|B_z|$ as a function of frequency # -# Plot mag(Bz) as a function of frequency for both coil positions. +# Plot $|B_z|$ as a function of frequency for both coil positions. # + variations = { @@ -241,7 +246,7 @@ ) # - -# ## Get simulation results from a solved setup +# ### Get simulation results from a solved setup # # Get simulation results from a solved setup as a ``SolutionData`` object. @@ -256,12 +261,12 @@ ) # - -# ## Set up sweep value and plot solution +# ### Set up sweep value and plot solution solutions.active_variation["Coil_Position"] = -0.02 solutions.plot() -# ## Change sweep value and plot solution +# ### Change sweep value and plot solution # # Change the sweep value and plot the solution again. # Uncomment to show plots. @@ -269,7 +274,7 @@ solutions.active_variation["Coil_Position"] = 0 # solutions.plot() -# ## Plot induced current density on surface of ladder plate +# ### Plot induced current density on surface of ladder plate # # Plot the induced current density, ``"Mag_J"``, on the surface of the ladder plate. @@ -282,14 +287,16 @@ plot_name="Mag_J", ) -# ## Release AEDT +# ## Finish +# +# ### Save the project m3d.save_project() m3d.release_desktop() # Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory. time.sleep(3) -# ## Clean up +# ### Clean up # # All project files are saved in the folder ``temp_folder.name``. # If you've run this example as a Jupyter notebook, you diff --git a/examples/template.py b/examples/template.py index 7cbf473be..3f25c0827 100644 --- a/examples/template.py +++ b/examples/template.py @@ -1,44 +1,62 @@ -# # Short, Descriptive Title (do not specify the application name here, i.e.: Maxwell2D, Maxwell3D etc) +# # Short, Descriptive Title # -# Description: +# (do not specify the solver type in the title, i.e.: Maxwell2D, Maxwell3D etc) # # Most examples can be described as a series of steps that comprise a workflow. # 1. Import packages and instantiate the application. -# 2. Do something useful and interesting. -# 3. View the results. +# 2. Do something useful and interesting like creating the model, assing materials and boundary conditions, etc. +# 3. Run one or more analyses. +# 4. View the results. # # Keywords: **Template**, **Jupyter** -# ## Perform imports and define constants +# ## Prerequisites # -# Perform required imports. +# ### Perform imports +# + import os import tempfile import time -import ansys.aedt.core +import ansys.aedt.core # Interface to Ansys Electronics Desktop +# - -# Define constants. +# ### Define constants +# Constants help ensure consistency and avoid repetition throughout the example. AEDT_VERSION = "2024.2" NUM_CORES = 4 NG_MODE = False # Open AEDT UI when it is launched. -# ## Create temporary directory + +# ### Create temporary directory +# +# Create a temporary working directory. +# The name of the working folder is stored in ``temp_folder.name``. # -# 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``. +# > **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. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") -# ## Launch AEDT and application +# ### Launch application # -# Create an instance of the application (such as ``Maxwell3d`` or``Hfss``) -# with a class (such as ``m3d`` or``hfss``) by providing -# the project and design names, the solver, and the version. +# AEDT applications are accessed through Python classes. +# Each application has it's own class, for example: +# - ``Maxwell3d`` +# - ``Hfss`` +# - ``Maxwell2d`` +# - ``Icepak`` +# - ``Emit`` +# - ``QExtractor`` +# +# > **Note:** Some examples access multiple applications. When the first +# > application is +# > started, an AEDT _Project_ is created. +# > When a 2nd application instance for a different AEDT +# > application is created, the corresponding design +# > type will be inserted into the project. project_name = os.path.join(temp_folder.name, "my_project.aedt") m3d = ansys.aedt.core.Maxwell3d( @@ -49,28 +67,55 @@ non_graphical=NG_MODE, new_desktop=True, ) -m3d.modeler.model_units = "mm" -# ## Preprocess +# ## Model Preparation +# +# Description of steps used to create and prepare the model for simulation. +# Add as many sections as needed for preprocessing tasks. Use level 3 headers +# for subsequent headers in this section. + +# ### Create 3D model +# +# > Insert code to build the model from scratch or import a model. +# +# ### Assign boundary conditions # -# Description of preprocessing task. -# Add as many sections as needed for preprocessing tasks. +# > Insert code to assign boundaries here. +# +# ### Define solution setup +# +# > Insert code to specify solver settings here. +# +# ### Run analysis +# +# > Run the simulation. +m3d.analyze_setup("Setup1") # ## Postprocess # -# Description of postprocessing task. -# Add as many sections as needed for postprocessing tasks. +# After generating results demonstrate how to visualize and evaluate results +# in this section. +# Level 3 headers can be used to identify various post-processing +# steps. +# +# ### Evaluate loss +# > For example, in this section you may use code to demonstrate how to evaluate loss. +# +# ### Visualize fields +# > PyAEDT provides access to field solution data via the -# ## Release AEDT +# ## Finish +# +# ### Save the project m3d.save_project() m3d.release_desktop() # Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory. time.sleep(3) -# ## Clean up +# ### Clean up # # All project files are saved in the folder ``temp_folder.name``. # If you've run this example as a Jupyter notebook, you diff --git a/requirements/requirements_doc.txt b/requirements/requirements_doc.txt index 3d36cfff6..262b652cb 100644 --- a/requirements/requirements_doc.txt +++ b/requirements/requirements_doc.txt @@ -5,7 +5,7 @@ git+https://github.com/ansys/pyaedt@main imageio==2.36.0 matplotlib==3.9.2 numpy==1.26.4 -openpyxl==3.1.4 +openpyxl==3.1.5 osmnx==1.9.0 pandas==2.2.0 pyvista==0.44.0