From 7eae896a1e6dd2514f6dfdf4138a3e83423bc287 Mon Sep 17 00:00:00 2001 From: Hui Zhou Date: Thu, 26 Oct 2023 14:56:03 +0200 Subject: [PATCH] terminal enhancement (#3795) * fix * fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyaedt/edb_core/edb_data/terminals.py Co-authored-by: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com> * Update pyaedt/edb_core/edb_data/terminals.py Co-authored-by: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com> * fix --------- Co-authored-by: ring630 <@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com> --- _unittest/test_00_EDB.py | 11 +++ pyaedt/edb.py | 13 ++- pyaedt/edb_core/edb_data/obj_base.py | 4 +- pyaedt/edb_core/edb_data/ports.py | 24 ------ pyaedt/edb_core/edb_data/terminals.py | 118 ++++++++++++++++++++++++-- pyaedt/edb_core/siwave.py | 44 +++++++++- 6 files changed, 177 insertions(+), 37 deletions(-) diff --git a/_unittest/test_00_EDB.py b/_unittest/test_00_EDB.py index c47d1346d6b..e79ebe9cbce 100644 --- a/_unittest/test_00_EDB.py +++ b/_unittest/test_00_EDB.py @@ -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" @@ -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", diff --git a/pyaedt/edb.py b/pyaedt/edb.py index 667b2ea3104..508fab190cc 100644 --- a/pyaedt/edb.py +++ b/pyaedt/edb.py @@ -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 @@ -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 @@ -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 @@ -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): diff --git a/pyaedt/edb_core/edb_data/obj_base.py b/pyaedt/edb_core/edb_data/obj_base.py index 4d43360e47a..45945326703 100644 --- a/pyaedt/edb_core/edb_data/obj_base.py +++ b/pyaedt/edb_core/edb_data/obj_base.py @@ -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): diff --git a/pyaedt/edb_core/edb_data/ports.py b/pyaedt/edb_core/edb_data/ports.py index 26e5d1271c7..a26d240be6f 100644 --- a/pyaedt/edb_core/edb_data/ports.py +++ b/pyaedt/edb_core/edb_data/ports.py @@ -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. diff --git a/pyaedt/edb_core/edb_data/terminals.py b/pyaedt/edb_core/edb_data/terminals.py index 2a996f08df1..39245883611 100644 --- a/pyaedt/edb_core/edb_data/terminals.py +++ b/pyaedt/edb_core/edb_data/terminals.py @@ -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 @@ -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): @@ -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. @@ -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 @@ -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 @@ -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 @@ -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) diff --git a/pyaedt/edb_core/siwave.py b/pyaedt/edb_core/siwave.py index 647f8afa3ef..8d6b65c0ced 100644 --- a/pyaedt/edb_core/siwave.py +++ b/pyaedt/edb_core/siwave.py @@ -7,14 +7,13 @@ from pyaedt.edb_core.edb_data.simulation_configuration import SimulationConfiguration from pyaedt.edb_core.edb_data.simulation_configuration import SourceType - -# from pyaedt.edb_core.edb_data.sources import SourceType from pyaedt.edb_core.edb_data.sources import CircuitPort from pyaedt.edb_core.edb_data.sources import CurrentSource from pyaedt.edb_core.edb_data.sources import DCTerminal from pyaedt.edb_core.edb_data.sources import PinGroup from pyaedt.edb_core.edb_data.sources import ResistorSource from pyaedt.edb_core.edb_data.sources import VoltageSource +from pyaedt.edb_core.general import BoundaryType from pyaedt.edb_core.general import convert_py_list_to_net_list from pyaedt.generic.constants import SolverType from pyaedt.generic.constants import SweepType @@ -1388,3 +1387,44 @@ def create_circuit_port_on_pin_group(self, pos_pin_group_name, neg_pin_group_nam neg_terminal.SetName(name + "_ref") pos_terminal.SetReferenceTerminal(neg_terminal) return True + + @pyaedt_function_handler + def place_voltage_probe( + self, + name, + positive_net_name, + positive_location, + positive_layer, + negative_net_name, + negative_location, + negative_layer, + ): + """Place a voltage probe between two points. + + Parameters + ---------- + name : str, + Name of the probe. + positive_net_name : str + Name of the positive net. + positive_location : list + Location of the positive terminal. + positive_layer : str, + Layer of the positive terminal. + negative_net_name : str, + Name of the negative net. + negative_location : list + Location of the negative terminal. + negative_layer : str + Layer of the negative terminal. + """ + from pyaedt.edb_core.edb_data.terminals import PointTerminal + + point_terminal = PointTerminal(self._pedb) + p_terminal = point_terminal.create(name, positive_net_name, positive_location, positive_layer) + p_terminal.boundary_type = BoundaryType.kVoltageProbe.name + + n_terminal = point_terminal.create(name + "_ref", negative_net_name, negative_location, negative_layer) + n_terminal.boundary_type = BoundaryType.kVoltageProbe.name + p_terminal.ref_terminal = n_terminal + return self._pedb.probes[name]