diff --git a/_unittest/test_00_EDB.py b/_unittest/test_00_EDB.py index 1f3abeff10..7620bd3257 100644 --- a/_unittest/test_00_EDB.py +++ b/_unittest/test_00_EDB.py @@ -886,6 +886,12 @@ def test_069_create_path(self): assert trace assert isinstance(trace.get_center_line(), list) assert isinstance(trace.get_center_line(True), list) + self.edbapp["delta_x"] = "1mm" + assert trace.add_point("delta_x", "1mm", True) + assert trace.get_center_line(True)[-1][0] == "(delta_x)+(0.025)" + assert trace.add_point(0.001, 0.002) + assert trace.get_center_line()[-1] == [0.001, 0.002] + def test_070_create_outline(self): edbapp = Edb( @@ -2896,6 +2902,9 @@ def test_147_find_dc_shorts(self): edbapp = Edb(target_path, edbversion=desktop_version) dc_shorts = edbapp.layout_validation.dc_shorts() assert dc_shorts + edbapp.nets.nets["DDR4_A0"].name = "DDR4$A0" + edbapp.layout_validation.illegal_net_names(True) + # assert len(dc_shorts) == 20 assert ["LVDS_CH09_N", "GND"] in dc_shorts assert ["LVDS_CH09_N", "DDR4_DM3"] in dc_shorts diff --git a/src/pyedb/legacy/edb_core/dotnet/database.py b/src/pyedb/legacy/edb_core/dotnet/database.py index 70111aeac7..b4e0479922 100644 --- a/src/pyedb/legacy/edb_core/dotnet/database.py +++ b/src/pyedb/legacy/edb_core/dotnet/database.py @@ -80,6 +80,39 @@ def arcs(self): # pragma: no cover """List of Edb.Geometry.ArcData.""" return list(self.edb_api.GetArcData()) + def get_points(self): + """Get all points in polygon. + Returns + ------- + list[list[edb_value]] + """ + + return [[self._pedb.edb_value(i.X), self._pedb.edb_value(i.Y)] for i in list(self.edb_api.Points)] + + def add_point(self, x, y, incremental=False): + """Add a point at the end of the point list of the polygon. + Parameters + ---------- + x: str, int, float + X coordinate. + y: str, in, float + Y coordinate. + incremental: bool + Whether to add the point incrementally. The default value is ``False``. When + ``True``, the coordinates of the added point are incremental to the last point. + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + """ + if incremental: + x = self._pedb.edb_value(x) + y = self._pedb.edb_value(y) + last_point = self.get_points()[-1] + x = "({})+({})".format(x, last_point[0].ToString()) + y = "({})+({})".format(y, last_point[1].ToString()) + return self.edb_api.AddPoint(GeometryDotNet(self._pedb).point_data(x, y)) + def get_bbox_of_boxes(self, points): """Get the EDB .NET API ``Edb.Geometry.GetBBoxOfBoxes`` database. diff --git a/src/pyedb/legacy/edb_core/edb_data/primitives_data.py b/src/pyedb/legacy/edb_core/edb_data/primitives_data.py index 3d3f09a1c1..6f3f508ac2 100644 --- a/src/pyedb/legacy/edb_core/edb_data/primitives_data.py +++ b/src/pyedb/legacy/edb_core/edb_data/primitives_data.py @@ -1,14 +1,15 @@ import math -from pyedb.edb_core.dotnet.primitive import BondwireDotNet -from pyedb.edb_core.dotnet.primitive import CircleDotNet -from pyedb.edb_core.dotnet.primitive import PathDotNet -from pyedb.edb_core.dotnet.primitive import PolygonDotNet -from pyedb.edb_core.dotnet.primitive import RectangleDotNet -from pyedb.edb_core.dotnet.primitive import TextDotNet -from pyedb.edb_core.edb_data.connectable import Connectable -from pyedb.edb_core.general import convert_py_list_to_net_list -from pyedb.generic.general_methods import pyaedt_function_handler +from pyedb.legacy.edb_core.dotnet.primitive import BondwireDotNet +from pyedb.legacy.edb_core.dotnet.primitive import CircleDotNet +from pyedb.legacy.edb_core.dotnet.primitive import PathDotNet +from pyedb.legacy.edb_core.dotnet.primitive import PolygonDataDotNet +from pyedb.legacy.edb_core.dotnet.primitive import PolygonDotNet +from pyedb.legacy.edb_core.dotnet.primitive import RectangleDotNet +from pyedb.legacy.edb_core.dotnet.primitive import TextDotNet +from pyedb.legacy.edb_core.edb_data.connectable import Connectable +from pyedb.legacy.edb_core.general import convert_py_list_to_net_list +from pyedb.generic.general_methods import pyedb_function_handler from pyedb.modeler.geometry_operators import GeometryOperators @@ -171,7 +172,7 @@ class EDBPrimitives(EDBPrimitivesMain): def __init__(self, raw_primitive, core_app): EDBPrimitivesMain.__init__(self, raw_primitive, core_app) - @pyaedt_function_handler() + @pyedb_function_handler() def area(self, include_voids=True): """Return the total area. @@ -331,7 +332,7 @@ def center(self): bbox = self.bbox return [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2] - @pyaedt_function_handler() + @pyedb_function_handler() def is_arc(self, point): """Either if a point is an arc or not. @@ -341,7 +342,7 @@ def is_arc(self, point): """ return point.IsArc() - @pyaedt_function_handler() + @pyedb_function_handler() def get_connected_object_id_set(self): """Produce a list of all geometries physically connected to a given layout object. @@ -354,7 +355,7 @@ def get_connected_object_id_set(self): layoutObjInst = layoutInst.GetLayoutObjInstance(self.primitive_object, None) # 2nd arg was [] return [loi.GetLayoutObj().GetId() for loi in layoutInst.GetConnectedObjects(layoutObjInst).Items] - @pyaedt_function_handler() + @pyedb_function_handler() def convert_to_polygon(self): """Convert path to polygon. @@ -369,7 +370,7 @@ def convert_to_polygon(self): self.primitive_object.Delete() return polygon - @pyaedt_function_handler() + @pyedb_function_handler() def subtract(self, primitives): """Subtract active primitive with one or more primitives. @@ -423,7 +424,7 @@ def subtract(self, primitives): continue return new_polys - @pyaedt_function_handler() + @pyedb_function_handler() def intersect(self, primitives): """Intersect active primitive with one or more primitives. @@ -508,7 +509,7 @@ def intersect(self, primitives): continue return new_polys - @pyaedt_function_handler() + @pyedb_function_handler() def unite(self, primitives): """Unite active primitive with one or more primitives. @@ -563,7 +564,7 @@ def unite(self, primitives): continue return new_polys - @pyaedt_function_handler() + @pyedb_function_handler() def intersection_type(self, primitive): """Get intersection type between actual primitive and another primitive or polygon data. @@ -588,7 +589,7 @@ def intersection_type(self, primitive): pass return int(self.polygon_data.edb_api.GetIntersectionType(poly.edb_api)) - @pyaedt_function_handler() + @pyedb_function_handler() def is_intersecting(self, primitive): """Check if actual primitive and another primitive or polygon data intesects. @@ -602,7 +603,7 @@ def is_intersecting(self, primitive): """ return True if self.intersection_type(primitive) >= 1 else False - @pyaedt_function_handler() + @pyedb_function_handler() def get_closest_point(self, point): """Get the closest point of the primitive to the input data. @@ -620,7 +621,7 @@ def get_closest_point(self, point): p0 = self.polygon_data.edb_api.GetClosestPoint(point) return [p0.X.ToDouble(), p0.Y.ToDouble()] - @pyaedt_function_handler() + @pyedb_function_handler() def get_closest_arc_midpoint(self, point): """Get the closest arc midpoint of the primitive to the input data. @@ -715,7 +716,27 @@ def length(self): length += GeometryOperators.points_distance(center_line[pt_ind], center_line[pt_ind + 1]) return length - @pyaedt_function_handler + @pyedb_function_handler() + def add_point(self, x, y, incremental=False): + """Add a point at the end of the path. + Parameters + ---------- + x: str, int, float + X coordinate. + y: str, in, float + Y coordinate. + incremental: bool + Add point incrementally. If True, coordinates of the added point is incremental to the last point. + The default value is ``False``. + Returns + ------- + bool + """ + center_line = PolygonDataDotNet(self._pedb, self._edb_object.GetCenterLine()) + center_line.add_point(x, y, incremental) + return self._edb_object.SetCenterLine(center_line.edb_api) + + @pyedb_function_handler() def get_center_line(self, to_string=False): """Get the center line of the trace. @@ -733,7 +754,7 @@ def get_center_line(self, to_string=False): else: return [[p.X.ToDouble(), p.Y.ToDouble()] for p in list(self.primitive_object.GetCenterLine().Points)] - @pyaedt_function_handler + @pyedb_function_handler() def clone(self): """Clone a primitive object with keeping same definition and location. @@ -759,8 +780,8 @@ def clone(self): if cloned_path: return cloned_path - # @pyaedt_function_handler - @pyaedt_function_handler + # @pyedb_function_handler() + @pyedb_function_handler() def create_edge_port( self, name, @@ -831,7 +852,7 @@ def __init__(self, raw_primitive, core_app): EDBPrimitives.__init__(self, raw_primitive, core_app) PolygonDotNet.__init__(self, self._app, raw_primitive) - @pyaedt_function_handler + @pyedb_function_handler() def clone(self): """Clone a primitive object with keeping same definition and location. @@ -853,7 +874,7 @@ def clone(self): return cloned_poly return False - @pyaedt_function_handler() + @pyedb_function_handler() def in_polygon( self, point_data, @@ -892,7 +913,7 @@ def in_polygon( else: return False - # @pyaedt_function_handler() + # @pyedb_function_handler() # def add_void(self, point_list): # """Add a void to current primitive. # diff --git a/src/pyedb/legacy/edb_core/layout_validation.py b/src/pyedb/legacy/edb_core/layout_validation.py index 5bb045c908..b246ba26b1 100644 --- a/src/pyedb/legacy/edb_core/layout_validation.py +++ b/src/pyedb/legacy/edb_core/layout_validation.py @@ -1,7 +1,10 @@ -from pyedb.edb_core.edb_data.padstacks_data import EDBPadstackInstance -from pyedb.edb_core.edb_data.primitives_data import EDBPrimitives +import re + +from pyedb.legacy.edb_core.edb_data.padstacks_data import EDBPadstackInstance +from pyedb.legacy.edb_core.edb_data.primitives_data import EDBPrimitives from pyedb.generic.general_methods import generate_unique_name -from pyedb.generic.general_methods import pyaedt_function_handler +from pyedb.generic.general_methods import pyedb_function_handler + class LayoutValidation: @@ -10,7 +13,7 @@ class LayoutValidation: def __init__(self, pedb): self._pedb = pedb - @pyaedt_function_handler() + @pyedb_function_handler() def dc_shorts(self, net_list=None, fix=False): """Find DC shorts on layout. @@ -91,7 +94,7 @@ def dc_shorts(self, net_list=None, fix=False): i.net = temp_name return dc_shorts - @pyaedt_function_handler() + @pyedb_function_handler() def disjoint_nets( self, net_list=None, keep_only_main_net=False, clean_disjoints_less_than=0.0, order_by_area=False ): @@ -211,3 +214,21 @@ def area_calc(elem): self._pedb._logger.info_timer("Disjoint Cleanup Completed.", timer_start) return new_nets + + + def illegal_net_names(self, fix=False): + """Find and fix illegal net names.""" + pattern = r"[\(\)\\\/:;*?<>\'\"|`~$]" + + nets = self._pedb.nets.nets + + renamed_nets = [] + for net, val in nets.items(): + if re.findall(pattern, net): + renamed_nets.append(net) + if fix: + new_name = re.sub(pattern, "_", net) + val.name = new_name + + self._pedb._logger.info("Found {} illegal net names.".format(len(renamed_nets))) + return diff --git a/src/pyedb/legacy/edb_core/nets.py b/src/pyedb/legacy/edb_core/nets.py index 0a1aec87ef..ec6d311cdb 100644 --- a/src/pyedb/legacy/edb_core/nets.py +++ b/src/pyedb/legacy/edb_core/nets.py @@ -5,12 +5,13 @@ import time import warnings -from pyedb.edb_core.edb_data.nets_data import EDBNetsData -from pyedb.edb_core.general import convert_py_list_to_net_list +from pyedb.legacy.edb_core.edb_data.nets_data import EDBNetsData +from pyedb.legacy.edb_core.general import convert_py_list_to_net_list from pyedb.generic.constants import CSS4_COLORS from pyedb.generic.general_methods import generate_unique_name from pyedb.generic.general_methods import is_ironpython -from pyedb.generic.general_methods import pyaedt_function_handler +from pyedb.generic.general_methods import pyedb_function_handler + from pyedb.modeler.geometry_operators import GeometryOperators @@ -24,7 +25,7 @@ class EdbNets(object): >>> edb_nets = edbapp.nets """ - @pyaedt_function_handler() + @pyedb_function_handler() def __getitem__(self, name): """Get a net from the Edb project. @@ -44,7 +45,6 @@ def __getitem__(self, name): def __init__(self, p_edb): self._pedb = p_edb - self._nets = {} self._nets_by_comp_dict = {} self._comps_by_nets_dict = {} @@ -88,9 +88,10 @@ def nets(self): Dictionary of nets. """ + temp = {} for net in self._layout.nets: - self._nets[net.name] = EDBNetsData(net.api_object, self._pedb) - return self._nets + temp[net.name] = EDBNetsData(net.api_object, self._pedb) + return temp @property def netlist(self): @@ -163,7 +164,7 @@ def power(self): nets[net] = value return nets - @pyaedt_function_handler() + @pyedb_function_handler() def eligible_power_nets(self, threshold=0.3): """Return a list of nets calculated by area to be eligible for PWR/Ground net classification. It uses the same algorithm implemented in SIwave. @@ -218,7 +219,7 @@ def components_by_nets(self): self._comps_by_nets_dict[n] = [comp] return self._comps_by_nets_dict - @pyaedt_function_handler() + @pyedb_function_handler() def generate_extended_nets( self, resistor_below=10, @@ -429,7 +430,7 @@ def _get_points_for_plot(self, my_net_points): # fmt: on return x, y - @pyaedt_function_handler() + @pyedb_function_handler() def get_plot_data( self, nets, @@ -738,7 +739,7 @@ def get_plot_data( else: return objects_lists - @pyaedt_function_handler() + @pyedb_function_handler() def classify_nets(self, power_nets=None, signal_nets=None): """Reassign power/ground or signal nets based on list of nets. @@ -770,7 +771,7 @@ def classify_nets(self, power_nets=None, signal_nets=None): self.nets[net].net_object.SetIsPowerGround(False) return True - @pyaedt_function_handler() + @pyedb_function_handler() def plot( self, nets=None, @@ -844,7 +845,7 @@ def plot( axis_equal=True, ) - @pyaedt_function_handler() + @pyedb_function_handler() def is_power_gound_net(self, netname_list): """Determine if one of the nets in a list is power or ground. @@ -866,7 +867,7 @@ def is_power_gound_net(self, netname_list): return True return False - @pyaedt_function_handler() + @pyedb_function_handler() def get_dcconnected_net_list(self, ground_nets=["GND"], res_value=0.001): """Get the nets connected to the direct current through inductors. @@ -917,7 +918,7 @@ def get_dcconnected_net_list(self, ground_nets=["GND"], res_value=0.001): return dcconnected_net_list - @pyaedt_function_handler() + @pyedb_function_handler() def get_powertree(self, power_net_name, ground_nets): """Retrieve the power tree. @@ -977,14 +978,14 @@ def get_powertree(self, power_net_name, ground_nets): ] return component_list, component_list_columns, net_group - @pyaedt_function_handler() + @pyedb_function_handler() def get_net_by_name(self, net_name): """Find a net by name.""" edb_net = self._edb.cell.net.find_by_name(self._active_layout, net_name) if edb_net is not None: return edb_net - @pyaedt_function_handler() + @pyedb_function_handler() def delete_nets(self, netlist): """Delete one or more nets from EDB. @@ -1009,7 +1010,7 @@ def delete_nets(self, netlist): warnings.warn("Use :func:`delete` method instead.", DeprecationWarning) return self.delete(netlist=netlist) - @pyaedt_function_handler() + @pyedb_function_handler() def delete(self, netlist): """Delete one or more nets from EDB. @@ -1042,7 +1043,7 @@ def delete(self, netlist): nets_deleted.append(i.name) return nets_deleted - @pyaedt_function_handler() + @pyedb_function_handler() def find_or_create_net(self, net_name="", start_with="", contain="", end_with=""): """Find or create the net with the given name in the layout. @@ -1117,7 +1118,7 @@ def find_or_create_net(self, net_name="", start_with="", contain="", end_with="" nets_found = [self.nets[net].net_object for net in list(self.nets.keys()) if contain in net.lower()] return nets_found - @pyaedt_function_handler() + @pyedb_function_handler() def is_net_in_component(self, component_name, net_name): """Check if a net belongs to a component. @@ -1141,7 +1142,7 @@ def is_net_in_component(self, component_name, net_name): return True return False - @pyaedt_function_handler() + @pyedb_function_handler() def find_and_fix_disjoint_nets( self, net_list=None, keep_only_main_net=False, clean_disjoints_less_than=0.0, order_by_area=False ): @@ -1176,7 +1177,7 @@ def find_and_fix_disjoint_nets( net_list, keep_only_main_net, clean_disjoints_less_than, order_by_area ) - @pyaedt_function_handler() + @pyedb_function_handler() def merge_nets_polygons(self, net_list): """Convert paths from net into polygons, evaluate all connected polygons and perform the merge.