pyedb 0.55.0__py3-none-any.whl → 0.56.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyedb might be problematic. Click here for more details.

@@ -0,0 +1,394 @@
1
+ # Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ """
24
+ This module contains the array building feature from unit cell.
25
+ """
26
+ import itertools
27
+ from typing import Optional, Union
28
+
29
+ from pyedb import Edb
30
+
31
+
32
+ # ----------------------
33
+ # Public façade function
34
+ # ----------------------
35
+ def create_array_from_unit_cell(
36
+ edb: Edb,
37
+ x_number: int = 2,
38
+ y_number: int = 2,
39
+ offset_x: Optional[Union[int, float]] = None,
40
+ offset_y: Optional[Union[int, float]] = None,
41
+ ) -> bool:
42
+ """
43
+ Create a 2-D rectangular array from the current EDB unit cell.
44
+
45
+ The function duplicates every primitive (polygon, rectangle, circle), path,
46
+ padstack via, and component found in the active layout and places copies on
47
+ a regular grid defined by *offset_x* and *offset_y*. If the offsets are
48
+ omitted they are automatically derived from the bounding box of the first
49
+ primitive found on the layer called **outline** (case-insensitive).
50
+
51
+ Parameters
52
+ ----------
53
+ edb : pyedb.Edb
54
+ An open Edb instance whose active layout is used as the unit cell.
55
+ x_number : int, optional
56
+ Number of columns (X-direction). Must be > 0. Defaults to 2.
57
+ y_number : int, optional
58
+ Number of rows (Y-direction). Must be > 0. Defaults to 2.
59
+ offset_x : int | float | None, optional
60
+ Horizontal pitch (distance between cell origins). When *None* the
61
+ value is derived from the outline geometry.
62
+ offset_y : int | float | None, optional
63
+ Vertical pitch (distance between cell origins). When *None* the
64
+ value is derived from the outline geometry.
65
+
66
+ Returns
67
+ -------
68
+ bool
69
+ ``True`` if the operation completed successfully.
70
+
71
+ Raises
72
+ ------
73
+ ValueError
74
+ If *x_number* or *y_number* are non-positive.
75
+ RuntimeError
76
+ If no outline is found and the offsets were not supplied, or if the
77
+ outline is not a supported type (polygon/rectangle).
78
+
79
+ Notes
80
+ -----
81
+ The routine is technology-agnostic; it delegates all EDB-specific calls to
82
+ small adapter classes that handle either the **gRPC** or **.NET** back-end
83
+ transparently.
84
+
85
+ Examples
86
+ --------
87
+ >>> from pyedb import Edb
88
+ >>> edb = Edb("unit_cell.aedb")
89
+ >>> create_array_from_unit_cell(edb, x_number=4, y_number=3)
90
+ True
91
+ """
92
+ if edb.grpc:
93
+ adapter = _GrpcAdapter(edb)
94
+ else:
95
+ adapter = _DotNetAdapter(edb)
96
+ return __create_array_from_unit_cell_impl(edb, adapter, x_number, y_number, offset_x, offset_y)
97
+
98
+
99
+ # ------------------------------------------------------------------
100
+ # Implementation (technology-agnostic)
101
+ # ------------------------------------------------------------------
102
+ def __create_array_from_unit_cell_impl(
103
+ edb: Edb,
104
+ adapter: "_BaseAdapter",
105
+ x_number: int,
106
+ y_number: int,
107
+ offset_x: Optional[Union[int, float]],
108
+ offset_y: Optional[Union[int, float]],
109
+ ) -> bool:
110
+ """
111
+ Inner worker that performs the actual replication.
112
+
113
+ Parameters
114
+ ----------
115
+ edb : pyedb.Edb
116
+ Edb instance (already validated by the façade).
117
+ adapter : _BaseAdapter
118
+ Technology-specific adapter (gRPC or .NET).
119
+ x_number : int
120
+ Number of columns.
121
+ y_number : int
122
+ Number of rows.
123
+ offset_x : float
124
+ Absolute pitch in X (always resolved by the caller).
125
+ offset_y : float
126
+ Absolute pitch in Y (always resolved by the caller).
127
+
128
+ Returns
129
+ -------
130
+ bool
131
+ ``True`` when finished.
132
+ """
133
+ # ---------- Sanity & auto-pitch detection ----------
134
+ if x_number <= 0 or y_number <= 0:
135
+ raise ValueError("x_number and y_number must be positive integers")
136
+
137
+ if offset_x is None or offset_y is None:
138
+ edb.logger.info("Auto-detecting outline extents")
139
+ outline_prims = [p for p in edb.modeler.primitives if p.layer_name.lower() == "outline"]
140
+ if not outline_prims:
141
+ raise RuntimeError("No outline found. Provide offset_x / offset_y or add an 'Outline' layer primitive.")
142
+ outline = outline_prims[0]
143
+ if not adapter.is_supported_outline(outline):
144
+ raise RuntimeError("Outline primitive is not a polygon/rectangle. Provide offset_x / offset_y.")
145
+ offset_x, offset_y = adapter.pitch_from_outline(outline)
146
+
147
+ # ---------- Collect everything we have to replicate ----------
148
+ primitives = [p for p in edb.modeler.primitives if adapter.is_primitive_to_copy(p)]
149
+ paths = list(edb.modeler.paths)
150
+ vias = list(edb.padstacks.vias.values())
151
+ components = list(edb.components.instances.values())
152
+
153
+ # ---------- Replication loops ----------
154
+ edb.logger.info(f"Starting array replication {x_number}×{y_number}")
155
+ for i, j in itertools.product(range(x_number), range(y_number)):
156
+ if i == 0 and j == 0:
157
+ continue # original already exists
158
+
159
+ dx = edb.value(offset_x * i)
160
+ dy = edb.value(offset_y * j)
161
+
162
+ # Primitives & voids
163
+ for prim in primitives:
164
+ new_poly = adapter.duplicate_primitive(prim, dx, dy, i, j)
165
+ for void in prim.voids:
166
+ adapter.duplicate_void(new_poly, void, dx, dy)
167
+
168
+ # Paths
169
+ for path in paths:
170
+ adapter.duplicate_path(path, dx, dy, i, j)
171
+
172
+ # Stand-alone vias
173
+ for via in (v for v in vias if not v.component):
174
+ adapter.duplicate_standalone_via(via, dx, dy, i, j)
175
+
176
+ # Components
177
+ for comp in components:
178
+ adapter.duplicate_component(comp, dx, dy, i, j)
179
+
180
+ edb.logger.info("Array replication finished successfully")
181
+ return True
182
+
183
+
184
+ # ------------------------------------------------------------------
185
+ # Technology-specific adapters
186
+ # ------------------------------------------------------------------
187
+ class _BaseAdapter:
188
+ """Abstract adapter defining the required interface."""
189
+
190
+ def __init__(self, edb: Edb):
191
+ self.edb = edb
192
+
193
+ # ---- Outline helpers ----
194
+ def is_supported_outline(self, outline) -> bool:
195
+ """Return True when *outline* is a primitive type from which pitch can be inferred."""
196
+ raise NotImplementedError
197
+
198
+ def pitch_from_outline(self, outline) -> tuple[float, float]:
199
+ """
200
+ Compute the (offset_x, offset_y) pitch from the bounding box of *outline*.
201
+
202
+ Returns
203
+ -------
204
+ tuple[float, float]
205
+ (width, height) of the outline primitive in database units.
206
+ """
207
+ raise NotImplementedError
208
+
209
+ # ---- Duplication helpers ----
210
+ def is_primitive_to_copy(self, prim) -> bool:
211
+ """Return True when *prim* is a primitive that must be duplicated."""
212
+ raise NotImplementedError
213
+
214
+ def duplicate_primitive(self, prim, dx, dy, i, j):
215
+ """Return a new primitive translated by (dx, dy)."""
216
+ raise NotImplementedError
217
+
218
+ def duplicate_void(self, new_poly, void, dx, dy):
219
+ """Add a translated copy of *void* to *new_poly*."""
220
+ raise NotImplementedError
221
+
222
+ def duplicate_path(self, path, dx, dy, i, j):
223
+ """Create a translated copy of *path*."""
224
+ raise NotImplementedError
225
+
226
+ def duplicate_standalone_via(self, via, dx, dy, i, j):
227
+ """Create a translated copy of a stand-alone via."""
228
+ raise NotImplementedError
229
+
230
+ def duplicate_component(self, comp, dx, dy, i, j):
231
+ """Create a translated copy of *comp* (including its pins)."""
232
+ raise NotImplementedError
233
+
234
+
235
+ class _GrpcAdapter(_BaseAdapter):
236
+ """Adapter for the gRPC-based EDB back-end."""
237
+
238
+ def is_supported_outline(self, outline) -> bool:
239
+ return outline.type in {"polygon", "rectangle"}
240
+
241
+ def pitch_from_outline(self, outline):
242
+ bbox = outline.polygon_data.bbox()
243
+ return self.edb.value(bbox[1].x - bbox[0].x), self.edb.value(bbox[1].y - bbox[0].y)
244
+
245
+ def is_primitive_to_copy(self, prim):
246
+ return prim.type in {"polygon", "rectangle", "circle"}
247
+
248
+ def duplicate_primitive(self, prim, dx, dy, i, j):
249
+ moved_pd = prim.polygon_data.move((dx, dy))
250
+ return self.edb.modeler.create_polygon(
251
+ moved_pd,
252
+ layer_name=prim.layer.name,
253
+ net_name=prim.net.name,
254
+ )
255
+
256
+ def duplicate_void(self, new_poly, void, dx, dy):
257
+ new_poly.add_void(void.polygon_data.move((dx, dy)))
258
+
259
+ def duplicate_path(self, path, dx, dy, i, j):
260
+ moved_line = path.cast().center_line.move((dx, dy))
261
+ self.edb.modeler.create_trace(
262
+ moved_line,
263
+ width=path.width,
264
+ layer_name=path.layer.name,
265
+ net_name=path.net.name,
266
+ )
267
+
268
+ def duplicate_standalone_via(self, via, dx, dy, i, j):
269
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
270
+
271
+ pos = via.position
272
+ PadstackInstance.create(
273
+ self.edb.active_layout,
274
+ net=via.net,
275
+ name=f"{via.name}_i{i}_j{j}",
276
+ padstack_def=self.edb.padstacks.definitions[via.padstack_definition],
277
+ position_x=pos[0] + dx,
278
+ position_y=pos[1] + dy,
279
+ rotation=0.0,
280
+ top_layer=self.edb.stackup.layers[via.start_layer],
281
+ bottom_layer=self.edb.stackup.layers[via.stop_layer],
282
+ )
283
+
284
+ def duplicate_component(self, comp, dx, dy, i, j):
285
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
286
+
287
+ new_pins = []
288
+ for pin in comp.pins.values():
289
+ pos = pin.position
290
+ new_pin = PadstackInstance.create(
291
+ self.edb.active_layout,
292
+ net=pin.net,
293
+ name=f"{pin.name}_i{i}_j{j}",
294
+ padstack_def=self.edb.padstacks.definitions[pin.padstack_definition],
295
+ position_x=pos[0] + dx,
296
+ position_y=pos[1] + dy,
297
+ rotation=0.0,
298
+ top_layer=self.edb.stackup.layers[pin.start_layer],
299
+ bottom_layer=self.edb.stackup.layers[pin.stop_layer],
300
+ )
301
+ new_pins.append(new_pin)
302
+
303
+ if new_pins:
304
+ res = self.edb.value(comp.res_value) if hasattr(comp, "res_value") and comp.res_value else None
305
+ cap = self.edb.value(comp.cap_value) if hasattr(comp, "cap_value") and comp.cap_value else None
306
+ ind = self.edb.value(comp.ind_value) if hasattr(comp, "ind_value") and comp.ind_value else None
307
+ new_comp = self.edb.components.create(
308
+ pins=new_pins,
309
+ component_name=f"{comp.name}_array_{i}_{j}",
310
+ placement_layer=comp.placement_layer,
311
+ component_part_name=comp.part_name,
312
+ r_value=res,
313
+ l_value=ind,
314
+ c_value=cap,
315
+ )
316
+ if hasattr(comp, "component_property") and comp.component_property:
317
+ new_comp.component_property = comp.component_property
318
+
319
+
320
+ class _DotNetAdapter(_BaseAdapter):
321
+ """Adapter for the legacy .NET-based EDB back-end."""
322
+
323
+ def is_supported_outline(self, outline) -> bool:
324
+ return outline.type.lower() in {"polygon", "rectangle"}
325
+
326
+ def pitch_from_outline(self, outline):
327
+ bbox = outline.polygon_data.bounding_box
328
+ return self.edb.value(bbox[1][0] - bbox[0][0]), self.edb.value(bbox[1][1] - bbox[0][1])
329
+
330
+ def is_primitive_to_copy(self, prim):
331
+ return prim.type.lower() in {"polygon", "rectangle", "circle"}
332
+
333
+ def duplicate_primitive(self, prim, dx, dy, i, j):
334
+ from pyedb.dotnet.database.geometry.point_data import PointData
335
+
336
+ vector = PointData.create_from_xy(self.edb, x=dx, y=dy)
337
+ moved_pd = prim.polygon_data
338
+ moved_pd._edb_object.Move(vector._edb_object)
339
+ return self.edb.modeler.create_polygon(
340
+ moved_pd,
341
+ layer_name=prim.layer.name,
342
+ net_name=prim.net.name,
343
+ )
344
+
345
+ def duplicate_void(self, new_poly, void, dx, dy):
346
+ from pyedb.dotnet.database.geometry.point_data import PointData
347
+
348
+ vector = PointData.create_from_xy(self.edb, x=dx, y=dy)
349
+ void_polygon_data = void.polygon_data
350
+ void_polygon_data._edb_object.Move(vector._edb_object)
351
+ new_poly.add_void(void_polygon_data.points)
352
+
353
+ def duplicate_path(self, path, dx, dy, i, j):
354
+ from pyedb.dotnet.database.geometry.point_data import PointData
355
+
356
+ vector = PointData.create_from_xy(self.edb, x=dx, y=dy)
357
+ moved_path = path._edb_object.GetCenterLine()
358
+ moved_path.Move(vector._edb_object)
359
+ moved_path = [[pt.X.ToDouble(), pt.Y.ToDouble()] for pt in list(moved_path.Points)]
360
+ self.edb.modeler.create_trace(
361
+ path_list=list(moved_path),
362
+ width=path.width,
363
+ layer_name=path.layer.name,
364
+ net_name=path.net.name,
365
+ )
366
+
367
+ def duplicate_standalone_via(self, via, dx, dy, i, j):
368
+ pos = via.position
369
+ self.edb.padstacks.place(
370
+ [pos[0] + dx, pos[1] + dy],
371
+ via.padstack_definition,
372
+ via_name=f"{via.aedt_name}_i{i}_j{j}",
373
+ )
374
+
375
+ def duplicate_component(self, comp, dx, dy, i, j):
376
+ new_pins = []
377
+ for pin in comp.pins.values():
378
+ pos = pin.position
379
+ new_pin = self.edb.padstacks.place(
380
+ [pos[0] + dx, pos[1] + dy],
381
+ pin.padstack_definition,
382
+ via_name=f"{pin.aedt_name}_i{i}_j{j}",
383
+ )
384
+ new_pins.append(new_pin)
385
+
386
+ if new_pins:
387
+ new_comp = self.edb.components.create(
388
+ pins=new_pins,
389
+ component_name=f"{comp.name}_array_{i}_{j}",
390
+ placement_layer=comp.placement_layer,
391
+ component_part_name=comp.part_name,
392
+ )
393
+ if hasattr(comp, "component_property"):
394
+ new_comp._edb_object.SetComponentProperty(comp.component_property)
@@ -2,8 +2,8 @@
2
2
  from decimal import Decimal
3
3
  import json
4
4
  import math
5
- import random
6
5
  import re
6
+ import secrets
7
7
  import string
8
8
 
9
9
  from pyedb.generic.general_methods import settings
@@ -54,7 +54,7 @@ def random_string(length=6, only_digits=False, char_set=None): # pragma: no cov
54
54
  char_set = string.digits
55
55
  else:
56
56
  char_set = string.ascii_uppercase + string.digits
57
- random_str = "".join(random.choice(char_set) for _ in range(int(length)))
57
+ random_str = "".join(secrets.choice(char_set) for _ in range(int(length)))
58
58
  return random_str
59
59
 
60
60
 
@@ -85,9 +85,8 @@ def unique_string_list(element_list, only_string=True): # pragma: no cover
85
85
  pass
86
86
  raise Exception(error_message)
87
87
 
88
- if only_string:
89
- non_string_entries = [x for x in element_list if type(x) is not str]
90
- assert not non_string_entries, "Invalid list entries {} are not a string!".format(non_string_entries)
88
+ if only_string and any(not isinstance(x, str) for x in element_list):
89
+ raise TypeError("Invalid list entries, some elements are not of type string.")
91
90
 
92
91
  return element_list
93
92
 
@@ -104,10 +103,10 @@ def string_list(element_list): # pragma: no cover
104
103
  -------
105
104
 
106
105
  """
106
+ if not isinstance(element_list, (str, list)):
107
+ raise TypeError("Input must be a list or a string")
107
108
  if isinstance(element_list, str):
108
109
  element_list = [element_list]
109
- else:
110
- assert isinstance(element_list, str), "Input must be a list or a string"
111
110
  return element_list
112
111
 
113
112
 
@@ -57,9 +57,11 @@ def Edb(
57
57
  oproject=None,
58
58
  student_version=False,
59
59
  use_ppe=False,
60
+ map_file=None,
60
61
  technology_file=None,
61
62
  grpc=False,
62
63
  control_file=None,
64
+ layer_filter=None,
63
65
  ):
64
66
  """Provides the EDB application interface.
65
67
 
@@ -78,7 +80,7 @@ def Edb(
78
80
  isreadonly : bool, optional
79
81
  Whether to open EBD in read-only mode when it is
80
82
  owned by HFSS 3D Layout. The default is ``False``.
81
- edbversion : str, optional
83
+ version : str, optional
82
84
  Version of EDB to use. The default is ``"2021.2"``.
83
85
  isaedtowned : bool, optional
84
86
  Whether to launch EDB from HFSS 3D Layout. The
@@ -87,10 +89,20 @@ def Edb(
87
89
  Reference to the AEDT project object.
88
90
  student_version : bool, optional
89
91
  Whether to open the AEDT student version. The default is ``False.``
92
+ use_ppe : bool, optional
93
+ Whether to use PPE license. The default is ``False``.
90
94
  technology_file : str, optional
91
95
  Full path to technology file to be converted to xml before importing or xml. Supported by GDS format only.
92
96
  grpc : bool, optional
93
97
  Whether to enable gRPC. Default value is ``False``.
98
+ layer_filter: str,optional
99
+ Layer filter .txt file.
100
+ map_file : str, optional
101
+ Layer map .map file.
102
+ control_file : str, optional
103
+ Path to the XML file. The default is ``None``, in which case an attempt is made to find
104
+ the XML file in the same directory as the board file. To succeed, the XML file and board file
105
+ must have the same name. Only the extension differs.
94
106
 
95
107
  Returns
96
108
  -------
@@ -263,7 +275,6 @@ def Edb(
263
275
  if grpc is False and settings.edb_dll_path is not None:
264
276
  # Check if the user specified a .dll path
265
277
  settings.logger.info(f"Force to use .dll from {settings.edb_dll_path} defined in settings.")
266
- settings.specified_version = "unknown"
267
278
  elif version is None:
268
279
  if settings.specified_version is not None:
269
280
  settings.logger.info(f"Use {settings.specified_version} defined in settings.")
@@ -324,8 +335,10 @@ def Edb(
324
335
  isaedtowned=isaedtowned,
325
336
  oproject=oproject,
326
337
  use_ppe=use_ppe,
327
- technology_file=technology_file,
328
338
  control_file=control_file,
339
+ map_file=map_file,
340
+ technology_file=technology_file,
341
+ layer_filter=layer_filter,
329
342
  )
330
343
 
331
344
 
@@ -1,5 +1,5 @@
1
1
  import os
2
- import random
2
+ import secrets
3
3
  import shutil
4
4
  import string
5
5
 
@@ -44,7 +44,10 @@ class Scratch:
44
44
  self._volatile = volatile
45
45
  self._cleaned = True
46
46
  char_set = string.ascii_uppercase + string.digits
47
- self._scratch_path = os.path.normpath(os.path.join(local_path, "scratch" + "".join(random.sample(char_set, 6))))
47
+ generator = secrets.SystemRandom()
48
+ self._scratch_path = os.path.normpath(
49
+ os.path.join(local_path, "scratch" + "".join(secrets.SystemRandom.sample(generator, char_set, 6)))
50
+ )
48
51
  if os.path.exists(self._scratch_path):
49
52
  try:
50
53
  self.remove()
@@ -35,8 +35,8 @@ import itertools
35
35
  import logging
36
36
  import math
37
37
  import os
38
- import random
39
38
  import re
39
+ import secrets
40
40
  import string
41
41
  import sys
42
42
  import tempfile
@@ -254,7 +254,7 @@ def generate_unique_name(rootname, suffix="", n=6):
254
254
 
255
255
  """
256
256
  char_set = string.ascii_uppercase + string.digits
257
- uName = "".join(random.choice(char_set) for _ in range(n))
257
+ uName = "".join(secrets.choice(char_set) for _ in range(n))
258
258
  unique_name = rootname + "_" + uName
259
259
  if suffix:
260
260
  unique_name += "_" + suffix
pyedb/generic/settings.py CHANGED
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  import os
24
+ from pathlib import Path
24
25
  import re
25
26
  import sys
26
27
  import time
@@ -248,6 +249,9 @@ class Settings(object):
248
249
  @edb_dll_path.setter
249
250
  def edb_dll_path(self, value):
250
251
  if os.path.exists(value):
252
+ ver = Path(value).parts[-2]
253
+ version, release = ver[-3:-1], ver[-1]
254
+ self.specified_version = f"20{version}.{release}"
251
255
  self._edb_dll_path = value
252
256
 
253
257
  @property
@@ -1065,6 +1065,9 @@ class Components(object):
1065
1065
  ComponentGroup as GrpcComponentGroup,
1066
1066
  )
1067
1067
 
1068
+ if not pins:
1069
+ raise ValueError("Pins must be a list of PadstackInstance objects.")
1070
+
1068
1071
  if not component_name:
1069
1072
  component_name = generate_unique_name("Comp_")
1070
1073
  if component_part_name:
@@ -1074,7 +1077,10 @@ class Components(object):
1074
1077
  if not compdef:
1075
1078
  return False
1076
1079
  new_cmp = GrpcComponentGroup.create(self._active_layout, component_name, compdef.name)
1077
- hosting_component_location = pins[0].component.transform
1080
+ if hasattr(pins[0], "component") and pins[0].component:
1081
+ hosting_component_location = pins[0].component.transform
1082
+ else:
1083
+ hosting_component_location = None
1078
1084
  if not len(pins) == len(compdef.component_pins):
1079
1085
  self._pedb.logger.error(
1080
1086
  f"Number on pins {len(pins)} does not match component definition number "
@@ -1092,7 +1098,18 @@ class Components(object):
1092
1098
  if new_cmp_layer_name in self._pedb.stackup.signal_layers:
1093
1099
  new_cmp_placement_layer = self._pedb.stackup.signal_layers[new_cmp_layer_name]
1094
1100
  new_cmp.placement_layer = new_cmp_placement_layer
1095
- new_cmp.component_type = GrpcComponentType.OTHER
1101
+ if r_value:
1102
+ new_cmp.component_type = GrpcComponentType.RESISTOR
1103
+ is_rlc = True
1104
+ elif c_value:
1105
+ new_cmp.component_type = GrpcComponentType.CAPACITOR
1106
+ is_rlc = True
1107
+ elif l_value:
1108
+ new_cmp.component_type = GrpcComponentType.INDUCTOR
1109
+ is_rlc = True
1110
+ else:
1111
+ new_cmp.component_type = GrpcComponentType.OTHER
1112
+ is_rlc = False
1096
1113
  if is_rlc and len(pins) == 2:
1097
1114
  rlc = GrpcRlc()
1098
1115
  rlc.is_parallel = is_parallel
@@ -1125,7 +1142,8 @@ class Components(object):
1125
1142
  component_property = new_cmp.component_property
1126
1143
  component_property.model = rlc_model
1127
1144
  new_cmp.component_property = component_property
1128
- new_cmp.transform = hosting_component_location
1145
+ if hosting_component_location:
1146
+ new_cmp.transform = hosting_component_location
1129
1147
  new_edb_comp = Component(self._pedb, new_cmp)
1130
1148
  self._cmp[new_cmp.name] = new_edb_comp
1131
1149
  return new_edb_comp
@@ -1666,7 +1684,9 @@ class Components(object):
1666
1684
  if comp.partname == part_name:
1667
1685
  pass
1668
1686
  else:
1669
- pinlist = self._pedb.padstacks.get_instances(refdes)
1687
+ pinlist = list(self.instances[refdes].pins.values())
1688
+ if not pinlist:
1689
+ continue
1670
1690
  if not part_name in self.definitions:
1671
1691
  comp_def = ComponentDef.create(self._db, part_name, None)
1672
1692
  # for pin in range(len(pinlist)):