Skip to content

Commit

Permalink
Merge branch 'main' into adm_aedt_file_3dcomponents_cell
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuelopez-ansys authored Oct 26, 2023
2 parents 0a94fca + 7eae896 commit 29f67c2
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 140 deletions.
11 changes: 11 additions & 0 deletions _unittest/test_00_EDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,15 @@ def test_042_create_current_source(self):
self.edbapp.siwave.create_pin_group(reference_designator="U1", pin_numbers=["A14", "A15"], group_name="vp_neg")
assert self.edbapp.siwave.create_voltage_probe_on_pin_group("vprobe", "vp_pos", "vp_neg")
assert self.edbapp.probes["vprobe"]
self.edbapp.siwave.place_voltage_probe(
"vprobe_2", "1V0", ["112mm", "24mm"], "1_Top", "GND", ["112mm", "27mm"], "Inner1(GND1)"
)
vprobe_2 = self.edbapp.probes["vprobe_2"]
ref_term = vprobe_2.ref_terminal
assert isinstance(ref_term.location, list)
ref_term.location = [0, 0]
assert ref_term.layer
ref_term.layer = "1_Top"

def test_043_create_dc_terminal(self):
assert self.edbapp.siwave.create_dc_terminal("U1", "DDR4_DQ40", "dc_terminal1") == "dc_terminal1"
Expand Down Expand Up @@ -1602,6 +1611,8 @@ def test_120_edb_create_port(self):
port_ver = edb.ports["port_ver"]
assert not port_ver.is_null
assert port_ver.hfss_type == "Gap"
port_hori = edb.ports["port_hori"]
assert port_hori.ref_terminal

args = {
"layer_name": "1_Top",
Expand Down
4 changes: 4 additions & 0 deletions _unittest/test_12_1_PostProcessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ def test_15_export_plot(self):
show=False, export_path=os.path.join(self.local_scratch.path, "image.jpg")
)
assert os.path.exists(obj.image_file)
obj2 = self.aedtapp.post.plot_model_obj(
show=False, export_path=os.path.join(self.local_scratch.path, "image2.jpg"), plot_as_separate_objects=False
)
assert os.path.exists(obj2.image_file)

@pytest.mark.skipif(is_linux or sys.version_info < (3, 8), reason="Not running in ironpython")
def test_16_create_field_plot(self):
Expand Down
54 changes: 50 additions & 4 deletions examples/05-Q3D/Q3D_DC_IR.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@
# Use previously calculated positions to identify faces,
# select the net "1_Top" and
# assign sources and sinks on nets.

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

identified_net = q3d.nets[0]
Expand All @@ -173,9 +173,11 @@

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

q3d.edit_sources(dcrl={"{}:{}".format(source1.props["Net"], source1.name): "1.0A",
"{}:{}".format(source2.props["Net"], source2.name): "1.0A"})
q3d.edit_sources(dcrl={"{}:{}".format(source1.props["Net"], source1.name): "-1.0A",
"{}:{}".format(source2.props["Net"], source2.name): "-1.0A",
"{}:{}".format(source2.props["Net"], source3.name): "-1.0A"})

###############################################################################
# Create setup
Expand All @@ -191,13 +193,27 @@
setup.props["DC"]["Cond"]["MaxPass"]=3
setup.analyze()

###############################################################################
# Field Calculator
# ~~~~~~~~~~~~~~~~
# We will create a named expression using field calculator.

drop_name = "Vdrop3_3"
fields = q3d.ofieldsreporter
q3d.ofieldsreporter.CalcStack("clear")
q3d.ofieldsreporter.EnterQty("Phidc")
q3d.ofieldsreporter.EnterScalar(3.3)
q3d.ofieldsreporter.CalcOp("+")
q3d.ofieldsreporter.AddNamedExpression(drop_name, "DC R/L Fields")

###############################################################################
# Phi plot
# ~~~~~~~~
# Compute ACL solutions and plot them.

plot1 = q3d.post.create_fieldplot_surface(q3d.modeler.get_objects_by_material("copper"), "Phidc",
plot1 = q3d.post.create_fieldplot_surface(q3d.modeler.get_objects_by_material("copper"), quantityName=drop_name,
intrinsincDict={"Freq": "1GHz"})

q3d.post.plot_field_from_fieldplot(
plot1.name,
project_path=q3d.working_directory,
Expand All @@ -209,6 +225,36 @@
log_scale=False,
)


###############################################################################
# Computing Voltage on Source Circles
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Using Field Calculator we can compute the voltage on source circles and get the value
# using get_solution_data method.

curves = []
for source_circle, source_bound in zip(sources_objs, sources_bounds):
source_sheet_name = source_circle.name

curves.append("V{}".format(source_bound.name))

q3d.ofieldsreporter.CalcStack("clear")
q3d.ofieldsreporter.CopyNamedExprToStack(drop_name)
q3d.ofieldsreporter.EnterSurf(source_sheet_name)
q3d.ofieldsreporter.CalcOp("Maximum")
q3d.ofieldsreporter.AddNamedExpression("V{}".format(source_bound.name), "DC R/L Fields")


data = q3d.post.get_solution_data(
curves,
q3d.nominal_adaptive,
variations={"Freq": "1GHz"},
report_category="DC R/L Fields",
)
for curve in curves:
print(data.data_real(curve))


###############################################################################
# Close AEDT
# ~~~~~~~~~~
Expand Down
4 changes: 2 additions & 2 deletions examples/06-Multiphysics/Hfss_Icepak_Coupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@
log_scale=True,
)
animated.gif_file = os.path.join(aedtapp.working_directory, "animate.gif")
animated.camera_position = [0, 0, 300]
animated.focal_point = [0, 0, 0]
# animated.camera_position = [0, 0, 300]
# animated.focal_point = [0, 0, 0]
# Set off_screen to False to visualize the animation.
# animated.off_screen = False
animated.animate()
Expand Down
13 changes: 10 additions & 3 deletions pyaedt/edb.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from pyaedt.edb_core.edb_data.hfss_simulation_setup_data import HfssSimulationSetup
from pyaedt.edb_core.edb_data.ports import BundleWavePort
from pyaedt.edb_core.edb_data.ports import CoaxPort
from pyaedt.edb_core.edb_data.ports import ExcitationProbes
from pyaedt.edb_core.edb_data.ports import ExcitationSources
from pyaedt.edb_core.edb_data.ports import GapPort
from pyaedt.edb_core.edb_data.ports import WavePort
Expand All @@ -35,8 +34,10 @@
from pyaedt.edb_core.edb_data.terminals import BundleTerminal
from pyaedt.edb_core.edb_data.terminals import EdgeTerminal
from pyaedt.edb_core.edb_data.terminals import PadstackInstanceTerminal
from pyaedt.edb_core.edb_data.terminals import PinGroupTerminal
from pyaedt.edb_core.edb_data.terminals import Terminal
from pyaedt.edb_core.edb_data.variables import Variable
from pyaedt.edb_core.general import BoundaryType
from pyaedt.edb_core.general import LayoutObjType
from pyaedt.edb_core.general import Primitives
from pyaedt.edb_core.general import TerminalType
Expand Down Expand Up @@ -364,6 +365,8 @@ def terminals(self):
ter = BundleTerminal(self, i)
elif terminal_type == TerminalType.PadstackInstanceTerminal.name:
ter = PadstackInstanceTerminal(self, i)
elif terminal_type == TerminalType.PinGroupTerminal.name:
ter = PinGroupTerminal(self, i)
else:
ter = Terminal(self, i)
temp[ter.name] = ter
Expand Down Expand Up @@ -424,8 +427,12 @@ def sources(self):
@property
def probes(self):
"""Get all layout sources."""
terms = [term for term in self.layout.terminals if int(term.GetBoundaryType()) in [8]]
return {ter.GetName(): ExcitationProbes(self, ter) for ter in terms}
temp = {}
for name, val in self.terminals.items():
if val.boundary_type == BoundaryType.kVoltageProbe.name:
if not val.is_reference_terminal:
temp[name] = val
return temp

@pyaedt_function_handler()
def open_edb(self):
Expand Down
4 changes: 2 additions & 2 deletions pyaedt/edb_core/edb_data/obj_base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
class ObjBase(object):
"""Manages EDB functionalities for a base object."""

def __init__(self, pedb, model):
def __init__(self, pedb, edb_object):
self._pedb = pedb
self._edb_object = model
self._edb_object = edb_object

@property
def is_null(self):
Expand Down
24 changes: 0 additions & 24 deletions pyaedt/edb_core/edb_data/ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,30 +176,6 @@ def phase(self, value):
self._edb_object.SetSourcePhase(self._edb.utility.value(value))


class ExcitationProbes(Terminal):
"""Manage probes properties.
Parameters
----------
pedb : pyaedt.edb.Edb
Edb object from Edblib.
edb_terminal : Ansys.Ansoft.Edb.Cell.Terminal.EdgeTerminal
Edge terminal instance from Edb.
Examples
--------
This example shows how to access this class.
>>> from pyaedt import Edb
>>> edb = Edb("myaedb.aedb")
>>> probes = edb.probes
>>> print(probes["Probe1"].name)
"""

def __init__(self, pedb, edb_terminal):
Terminal.__init__(self, pedb, edb_terminal)


class BundleWavePort(BundleTerminal):
"""Manages bundle wave port properties.
Expand Down
118 changes: 112 additions & 6 deletions pyaedt/edb_core/edb_data/terminals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pyaedt.edb_core.edb_data.connectable import Connectable
from pyaedt.edb_core.edb_data.padstacks_data import EDBPadstackInstance
from pyaedt.edb_core.edb_data.primitives_data import cast
from pyaedt.edb_core.general import BoundaryType
from pyaedt.edb_core.general import TerminalType
from pyaedt.edb_core.general import convert_py_list_to_net_list
from pyaedt.generic.general_methods import generate_unique_name
Expand Down Expand Up @@ -135,7 +136,17 @@ def boundary_type(self):
-------
int
"""
return self._edb_object.GetBoundaryType()
return self._edb_object.GetBoundaryType().ToString()

@boundary_type.setter
def boundary_type(self, value):
if not value in [i.name for i in BoundaryType]: # pragma : no cover
self._pedb.logger.warning("Invalid Boundary Type={}".format(value))
if value == self._pedb.edb_api.cell.terminal.BoundaryType.kVoltageProbe.ToString():
temp = self._pedb.edb_api.cell.terminal.BoundaryType.kVoltageProbe
else: # pragma : no cover
temp = self._pedb.edb_api.cell.terminal.BoundaryType.InvalidBoundary
self._edb_object.SetBoundaryType(temp)

@property
def impedance(self):
Expand All @@ -146,6 +157,28 @@ def impedance(self):
def impedance(self, value):
self._edb_object.SetImpedance(self._pedb.edb_value(value))

@property
def is_reference_terminal(self):
"""Whether it is a reference terminal."""
return self._edb_object.IsReferenceTerminal()

@property
def ref_terminal(self):
"""Get reference terminal."""

terminal = Terminal(self._pedb, self._edb_object.GetReferenceTerminal())
if not terminal.is_null:
if terminal.terminal_type == TerminalType.PointTerminal.name:
return PointTerminal(self._pedb, terminal._edb_object)
elif terminal.terminal_type == TerminalType.EdgeTerminal.name:
return EdgeTerminal(self._pedb, terminal._edb_object)
elif terminal.terminal_type == TerminalType.InvalidTerminal.name: # pragma : no cover
return None

@ref_terminal.setter
def ref_terminal(self, value):
self._edb_object.SetReferenceTerminal(value._edb_object)

@property
def reference_object(self): # pragma : no cover
"""This returns the object assigned as reference. It can be a primitive or a padstack instance.
Expand Down Expand Up @@ -244,7 +277,7 @@ def get_pin_group_terminal_reference_pin(self, gnd_net_name_preference=None): #
return EDBPadstackInstance(refTermPSI, self._pedb)
except AttributeError:
return None
return None # pragma: no cover
return None

@pyaedt_function_handler()
def get_edge_terminal_reference_primitive(self): # pragma : no cover
Expand All @@ -268,7 +301,7 @@ def get_edge_terminal_reference_primitive(self): # pragma : no cover
prim_shape_data = primitive.GetPolygonData()
if prim_shape_data.PointInPolygon(shape_pd):
return cast(primitive, self._pedb)
return None # pragma: no cover
return None

@pyaedt_function_handler()
def get_point_terminal_reference_primitive(self): # pragma : no cover
Expand Down Expand Up @@ -331,11 +364,11 @@ def _get_closest_pin(self, ref_pin, pin_list, gnd_net=None):
else:
power_ground_net_names = [net for net in self._pedb.nets.power_nets.keys()]
comp_ref_pins = [i for i in pin_list if i.GetNet().GetName() in power_ground_net_names]
if len(comp_ref_pins) == 0:
if len(comp_ref_pins) == 0: # pragma: no cover
self._pedb.logger.error(
"Terminal with PadStack Instance Name {} component has no reference pins.".format(ref_pin.GetName())
) # pragma: no cover
return None # pragma: no cover
)
return None
closest_pin_distance = None
pin_obj = None
for pin in comp_ref_pins: # find the distance to all the pins to the terminal pin
Expand Down Expand Up @@ -456,3 +489,76 @@ def create(self, padstack_instance, name=None, layer=None, is_ref=False):
terminal = PadstackInstanceTerminal(self._pedb, terminal)

return terminal if not terminal.is_null else False


class PointTerminal(Terminal):
"""Manages point terminal properties."""

def __init__(self, pedb, edb_object=None):
super().__init__(pedb, edb_object)

@pyaedt_function_handler
def create(self, name, net, location, layer, is_ref=False):
"""Create a point terminal.
Parameters
----------
name : str
Name of the terminal.
net : str
Name of the net.
location : list
Location of the terminal.
layer : str
Name of the layer.
is_ref : bool, optional
Whether it is a reference terminal.
Returns
-------
"""
terminal = self._pedb.edb_api.cell.terminal.PointTerminal.Create(
self._pedb.active_layout,
self._pedb.nets[net].net_object,
name,
self._pedb.point_data(*location),
self._pedb.stackup[layer]._edb_layer,
is_ref,
)
terminal = PointTerminal(self._pedb, terminal)
return terminal if not terminal.is_null else False

@property
def location(self):
"""Get location of the terminal."""
point_data = self._pedb.point_data(0, 0)
layer = list(self._pedb.stackup.layers.values())[0]._edb_layer
if self._edb_object.GetParameters(point_data, layer):
return [point_data.X.ToDouble(), point_data.Y.ToDouble()]

@location.setter
def location(self, value):
layer = self.layer
self._edb_object.SetParameters(self._pedb.point_data(*value), layer)

@property
def layer(self):
"""Get layer of the terminal."""
point_data = self._pedb.point_data(0, 0)
layer = list(self._pedb.stackup.layers.values())[0]._edb_layer
if self._edb_object.GetParameters(point_data, layer):
return layer

@layer.setter
def layer(self, value):
layer = self._pedb.stackup.layers[value]._edb_layer
point_data = self._pedb.point_data(*self.location)
self._edb_object.SetParameters(point_data, layer)


class PinGroupTerminal(Terminal):
"""Manages pin group terminal properties."""

def __init__(self, pedb, edb_object=None):
super().__init__(pedb, edb_object)
Loading

0 comments on commit 29f67c2

Please sign in to comment.