pyedb 0.2.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.

Files changed (128) hide show
  1. pyedb/__init__.py +17 -0
  2. pyedb/dotnet/__init__.py +0 -0
  3. pyedb/dotnet/application/Variables.py +2261 -0
  4. pyedb/dotnet/application/__init__.py +0 -0
  5. pyedb/dotnet/clr_module.py +103 -0
  6. pyedb/dotnet/edb.py +4237 -0
  7. pyedb/dotnet/edb_core/__init__.py +1 -0
  8. pyedb/dotnet/edb_core/cell/__init__.py +0 -0
  9. pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
  10. pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
  11. pyedb/dotnet/edb_core/components.py +2669 -0
  12. pyedb/dotnet/edb_core/configuration.py +423 -0
  13. pyedb/dotnet/edb_core/definition/__init__.py +0 -0
  14. pyedb/dotnet/edb_core/definition/component_def.py +166 -0
  15. pyedb/dotnet/edb_core/definition/component_model.py +30 -0
  16. pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
  17. pyedb/dotnet/edb_core/definition/definitions.py +12 -0
  18. pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
  19. pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
  20. pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
  21. pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
  22. pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
  23. pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
  24. pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
  25. pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
  26. pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
  27. pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
  28. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
  29. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
  30. pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
  31. pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
  32. pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
  33. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
  34. pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
  35. pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
  36. pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
  37. pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
  38. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
  39. pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
  40. pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
  41. pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
  42. pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
  43. pyedb/dotnet/edb_core/general.py +181 -0
  44. pyedb/dotnet/edb_core/hfss.py +1646 -0
  45. pyedb/dotnet/edb_core/layout.py +1244 -0
  46. pyedb/dotnet/edb_core/layout_validation.py +272 -0
  47. pyedb/dotnet/edb_core/materials.py +939 -0
  48. pyedb/dotnet/edb_core/net_class.py +335 -0
  49. pyedb/dotnet/edb_core/nets.py +1215 -0
  50. pyedb/dotnet/edb_core/padstack.py +1389 -0
  51. pyedb/dotnet/edb_core/siwave.py +1427 -0
  52. pyedb/dotnet/edb_core/stackup.py +2703 -0
  53. pyedb/edb_logger.py +396 -0
  54. pyedb/generic/__init__.py +0 -0
  55. pyedb/generic/constants.py +1063 -0
  56. pyedb/generic/data_handlers.py +320 -0
  57. pyedb/generic/design_types.py +104 -0
  58. pyedb/generic/filesystem.py +150 -0
  59. pyedb/generic/general_methods.py +1535 -0
  60. pyedb/generic/plot.py +1840 -0
  61. pyedb/generic/process.py +285 -0
  62. pyedb/generic/settings.py +224 -0
  63. pyedb/ipc2581/__init__.py +0 -0
  64. pyedb/ipc2581/bom/__init__.py +0 -0
  65. pyedb/ipc2581/bom/bom.py +21 -0
  66. pyedb/ipc2581/bom/bom_item.py +32 -0
  67. pyedb/ipc2581/bom/characteristics.py +37 -0
  68. pyedb/ipc2581/bom/refdes.py +16 -0
  69. pyedb/ipc2581/content/__init__.py +0 -0
  70. pyedb/ipc2581/content/color.py +38 -0
  71. pyedb/ipc2581/content/content.py +55 -0
  72. pyedb/ipc2581/content/dictionary_color.py +29 -0
  73. pyedb/ipc2581/content/dictionary_fill.py +28 -0
  74. pyedb/ipc2581/content/dictionary_line.py +30 -0
  75. pyedb/ipc2581/content/entry_color.py +13 -0
  76. pyedb/ipc2581/content/entry_line.py +14 -0
  77. pyedb/ipc2581/content/fill.py +15 -0
  78. pyedb/ipc2581/content/layer_ref.py +10 -0
  79. pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
  80. pyedb/ipc2581/ecad/__init__.py +0 -0
  81. pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
  82. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
  83. pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
  84. pyedb/ipc2581/ecad/cad_data/component.py +41 -0
  85. pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
  86. pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
  87. pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
  88. pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
  89. pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
  90. pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
  91. pyedb/ipc2581/ecad/cad_data/package.py +104 -0
  92. pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
  93. pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
  94. pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
  95. pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
  96. pyedb/ipc2581/ecad/cad_data/path.py +89 -0
  97. pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
  98. pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
  99. pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
  100. pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
  101. pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
  102. pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
  103. pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
  104. pyedb/ipc2581/ecad/cad_data/step.py +275 -0
  105. pyedb/ipc2581/ecad/cad_header.py +33 -0
  106. pyedb/ipc2581/ecad/ecad.py +19 -0
  107. pyedb/ipc2581/ecad/spec.py +46 -0
  108. pyedb/ipc2581/history_record.py +37 -0
  109. pyedb/ipc2581/ipc2581.py +387 -0
  110. pyedb/ipc2581/logistic_header.py +25 -0
  111. pyedb/misc/__init__.py +0 -0
  112. pyedb/misc/aedtlib_personalib_install.py +14 -0
  113. pyedb/misc/downloads.py +322 -0
  114. pyedb/misc/misc.py +67 -0
  115. pyedb/misc/pyedb.runtimeconfig.json +13 -0
  116. pyedb/misc/siw_feature_config/__init__.py +0 -0
  117. pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
  118. pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
  119. pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
  120. pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
  121. pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
  122. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
  123. pyedb/misc/utilities.py +27 -0
  124. pyedb/modeler/geometry_operators.py +2082 -0
  125. pyedb-0.2.0.dist-info/LICENSE +21 -0
  126. pyedb-0.2.0.dist-info/METADATA +208 -0
  127. pyedb-0.2.0.dist-info/RECORD +128 -0
  128. pyedb-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,2669 @@
1
+ """This module contains the `Components` class.
2
+
3
+ """
4
+ import codecs
5
+ import json
6
+ import math
7
+ import re
8
+ import warnings
9
+
10
+ from pyedb.dotnet.clr_module import String
11
+ from pyedb.dotnet.edb_core.edb_data.components_data import EDBComponent
12
+ from pyedb.dotnet.edb_core.edb_data.padstacks_data import EDBPadstackInstance
13
+ from pyedb.dotnet.edb_core.edb_data.sources import Source, SourceType
14
+ from pyedb.dotnet.edb_core.definition.component_def import EDBComponentDef
15
+ from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
16
+ from pyedb.dotnet.edb_core.padstack import EdbPadstacks
17
+ from pyedb.generic.general_methods import (
18
+ _retry_ntimes,
19
+ get_filename_without_extension,
20
+ pyedb_function_handler,
21
+ )
22
+ from pyedb.modeler.geometry_operators import GeometryOperators
23
+
24
+
25
+ def resistor_value_parser(RValue):
26
+ """Convert a resistor value.
27
+
28
+ Parameters
29
+ ----------
30
+ RValue : float
31
+ Resistor value.
32
+
33
+ Returns
34
+ -------
35
+ float
36
+ Resistor value.
37
+
38
+ """
39
+ if isinstance(RValue, str):
40
+ RValue = RValue.replace(" ", "")
41
+ RValue = RValue.replace("meg", "m")
42
+ RValue = RValue.replace("Ohm", "")
43
+ RValue = RValue.replace("ohm", "")
44
+ RValue = RValue.replace("k", "e3")
45
+ RValue = RValue.replace("m", "e-3")
46
+ RValue = RValue.replace("M", "e6")
47
+ RValue = float(RValue)
48
+ return RValue
49
+
50
+
51
+ class Components(object):
52
+ """Manages EDB components and related method accessible from `Edb.components` property.
53
+
54
+ Parameters
55
+ ----------
56
+ edb_class : :class:`pyedb.dotnet.edb.Edb`
57
+
58
+ Examples
59
+ --------
60
+ >>> from pyedb import Edb
61
+ >>> edbapp = Edb("myaedbfolder")
62
+ >>> edbapp.components
63
+ """
64
+
65
+ @pyedb_function_handler()
66
+ def __getitem__(self, name):
67
+ """Get a component or component definition from the Edb project.
68
+
69
+ Parameters
70
+ ----------
71
+ name : str
72
+
73
+ Returns
74
+ -------
75
+ :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`
76
+
77
+ """
78
+ if name in self.instances:
79
+ return self.instances[name]
80
+ elif name in self.definitions:
81
+ return self.definitions[name]
82
+ self._pedb.logger.error("Component or definition not found.")
83
+ return
84
+
85
+ def __init__(self, p_edb):
86
+ self._pedb = p_edb
87
+ self._cmp = {}
88
+ self._res = {}
89
+ self._cap = {}
90
+ self._ind = {}
91
+ self._ios = {}
92
+ self._ics = {}
93
+ self._others = {}
94
+ self._pins = {}
95
+ self._comps_by_part = {}
96
+ self._init_parts()
97
+ self._padstack = EdbPadstacks(self._pedb)
98
+
99
+ @property
100
+ def _logger(self):
101
+ """Logger."""
102
+ return self._pedb.logger
103
+
104
+ @property
105
+ def _edb(self):
106
+ return self._pedb.edb_api
107
+
108
+ @pyedb_function_handler()
109
+ def _init_parts(self):
110
+ a = self.components
111
+ a = self.resistors
112
+ a = self.ICs
113
+ a = self.Others
114
+ a = self.inductors
115
+ a = self.IOs
116
+ a = self.components_by_partname
117
+ return True
118
+
119
+ def _get_edb_value(self, value):
120
+ return self._pedb.edb_value(value)
121
+
122
+ @property
123
+ def _edbutils(self):
124
+ return self._pedb.edbutils
125
+
126
+ @property
127
+ def _active_layout(self):
128
+ return self._pedb.active_layout
129
+
130
+ @property
131
+ def _layout(self):
132
+ return self._pedb.layout
133
+
134
+ @property
135
+ def _cell(self):
136
+ return self._pedb.cell
137
+
138
+ @property
139
+ def _db(self):
140
+ return self._pedb.active_db
141
+
142
+ @property
143
+ def components(self):
144
+ """Component setup information.
145
+
146
+ .. deprecated:: 0.6.62
147
+ Use new property :func:`instances` instead.
148
+
149
+ Returns
150
+ -------
151
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
152
+ Default dictionary for the EDB component.
153
+
154
+ Examples
155
+ --------
156
+
157
+ >>> from pyedb.dotnet.edb import Edb
158
+ >>> edbapp = Edb("myaedbfolder")
159
+ >>> edbapp.components.components
160
+
161
+ """
162
+ warnings.warn("Use new property :func:`instances` instead.", DeprecationWarning)
163
+ return self.instances
164
+
165
+ @property
166
+ def instances(self):
167
+ """All Cell components objects.
168
+
169
+ Returns
170
+ -------
171
+ Dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
172
+ Default dictionary for the EDB component.
173
+
174
+ Examples
175
+ --------
176
+
177
+ >>> from pyedb.dotnet.edb import Edb
178
+ >>> edbapp = Edb("myaedbfolder")
179
+ >>> edbapp.components.components
180
+
181
+ """
182
+ if not self._cmp:
183
+ self.refresh_components()
184
+ return self._cmp
185
+
186
+ @property
187
+ def definitions(self):
188
+ """Retrieve component definition list.
189
+
190
+ Returns
191
+ -------
192
+ dict of :class:`EDBComponentDef`"""
193
+ return {l.GetName(): EDBComponentDef(self._pedb, l) for l in list(self._pedb.component_defs)}
194
+
195
+ @property
196
+ def nport_comp_definition(self):
197
+ """Retrieve Nport component definition list."""
198
+ m = "Ansys.Ansoft.Edb.Definition.NPortComponentModel"
199
+ return {name: l for name, l in self.definitions.items() if m in [i.ToString() for i in l._comp_model]}
200
+
201
+ @pyedb_function_handler()
202
+ def import_definition(self, file_path):
203
+ """Import component definition from json file.
204
+
205
+ Parameters
206
+ ----------
207
+ file_path : str
208
+ File path of json file.
209
+ """
210
+ with codecs.open(file_path, "r", encoding="utf-8") as f:
211
+ data = json.load(f)
212
+ for part_name, p in data["Definitions"].items():
213
+ model_type = p["Model_type"]
214
+ if part_name not in self.definitions:
215
+ continue
216
+ comp_definition = self.definitions[part_name]
217
+ comp_definition.type = p["Component_type"]
218
+
219
+ if model_type == "RLC":
220
+ comp_definition.assign_rlc_model(p["Res"], p["Ind"], p["Cap"], p["Is_parallel"])
221
+ else:
222
+ model_name = p["Model_name"]
223
+ file_path = data[model_type][model_name]
224
+ if model_type == "SParameterModel":
225
+ if "Reference_net" in p:
226
+ reference_net = p["Reference_net"]
227
+ else:
228
+ reference_net = None
229
+ comp_definition.assign_s_param_model(file_path, model_name, reference_net)
230
+ elif model_type == "SPICEModel":
231
+ comp_definition.assign_spice_model(file_path, model_name)
232
+ else:
233
+ pass
234
+ return True
235
+
236
+ @pyedb_function_handler()
237
+ def export_definition(self, file_path):
238
+ """Export component definitions to json file.
239
+
240
+ Parameters
241
+ ----------
242
+ file_path : str
243
+ File path of json file.
244
+
245
+ Returns
246
+ -------
247
+
248
+ """
249
+ data = {
250
+ "SParameterModel": {},
251
+ "SPICEModel": {},
252
+ "Definitions": {},
253
+ }
254
+ for part_name, props in self.definitions.items():
255
+ comp_list = list(props.components.values())
256
+ if comp_list:
257
+ data["Definitions"][part_name] = {}
258
+ data["Definitions"][part_name]["Component_type"] = props.type
259
+ comp = comp_list[0]
260
+ data["Definitions"][part_name]["Model_type"] = comp.model_type
261
+ if comp.model_type == "RLC":
262
+ rlc_values = [i if i else 0 for i in comp.rlc_values]
263
+ data["Definitions"][part_name]["Res"] = rlc_values[0]
264
+ data["Definitions"][part_name]["Ind"] = rlc_values[1]
265
+ data["Definitions"][part_name]["Cap"] = rlc_values[2]
266
+ data["Definitions"][part_name]["Is_parallel"] = True if comp.is_parallel_rlc else False
267
+ else:
268
+ if comp.model_type == "SParameterModel":
269
+ model = comp.s_param_model
270
+ data["Definitions"][part_name]["Model_name"] = model.name
271
+ data["Definitions"][part_name]["Reference_net"] = model.reference_net
272
+ if not model.name in data["SParameterModel"]:
273
+ data["SParameterModel"][model.name] = model.file_path
274
+ elif comp.model_type == "SPICEModel":
275
+ model = comp.spice_model
276
+ data["Definitions"][part_name]["Model_name"] = model.name
277
+ if not model.name in data["SPICEModel"]:
278
+ data["SPICEModel"][model.name] = model.file_path
279
+ else:
280
+ model = comp.netlist_model
281
+ data["Definitions"][part_name]["Model_name"] = model.netlist
282
+
283
+ with codecs.open(file_path, "w", encoding="utf-8") as f:
284
+ json.dump(data, f, ensure_ascii=False, indent=4)
285
+ return file_path
286
+
287
+ @pyedb_function_handler()
288
+ def refresh_components(self):
289
+ """Refresh the component dictionary."""
290
+ # self._logger.info("Refreshing the Components dictionary.")
291
+ self._cmp = {
292
+ l.GetName(): EDBComponent(self._pedb, l)
293
+ for l in self._layout.groups
294
+ if l.ToString() == "Ansys.Ansoft.Edb.Cell.Hierarchy.Component"
295
+ }
296
+ return True
297
+
298
+ @property
299
+ def resistors(self):
300
+ """Resistors.
301
+
302
+ Returns
303
+ -------
304
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
305
+ Dictionary of resistors.
306
+
307
+ Examples
308
+ --------
309
+
310
+ >>> from pyedb import Edb
311
+ >>> edbapp = Edb("myaedbfolder")
312
+ >>> edbapp.components.resistors
313
+ """
314
+ self._res = {}
315
+ for el, val in self.instances.items():
316
+ if val.type == "Resistor":
317
+ self._res[el] = val
318
+ return self._res
319
+
320
+ @property
321
+ def capacitors(self):
322
+ """Capacitors.
323
+
324
+ Returns
325
+ -------
326
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
327
+ Dictionary of capacitors.
328
+
329
+ Examples
330
+ --------
331
+
332
+ >>> from pyedb import Edb
333
+ >>> edbapp = Edb("myaedbfolder")
334
+ >>> edbapp.components.capacitors
335
+ """
336
+ self._cap = {}
337
+ for el, val in self.instances.items():
338
+ if val.type == "Capacitor":
339
+ self._cap[el] = val
340
+ return self._cap
341
+
342
+ @property
343
+ def inductors(self):
344
+ """Inductors.
345
+
346
+ Returns
347
+ -------
348
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
349
+ Dictionary of inductors.
350
+
351
+ Examples
352
+ --------
353
+
354
+ >>> from pyedb import Edb
355
+ >>> edbapp = Edb("myaedbfolder")
356
+ >>> edbapp.components.inductors
357
+
358
+ """
359
+ self._ind = {}
360
+ for el, val in self.instances.items():
361
+ if val.type == "Inductor":
362
+ self._ind[el] = val
363
+ return self._ind
364
+
365
+ @property
366
+ def ICs(self):
367
+ """Integrated circuits.
368
+
369
+ Returns
370
+ -------
371
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
372
+ Dictionary of integrated circuits.
373
+
374
+ Examples
375
+ --------
376
+
377
+ >>> from pyedb import Edb
378
+ >>> edbapp = Edb("myaedbfolder")
379
+ >>> edbapp.components.ICs
380
+
381
+ """
382
+ self._ics = {}
383
+ for el, val in self.instances.items():
384
+ if val.type == "IC":
385
+ self._ics[el] = val
386
+ return self._ics
387
+
388
+ @property
389
+ def IOs(self):
390
+ """Circuit inupts and outputs.
391
+
392
+ Returns
393
+ -------
394
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
395
+ Dictionary of circuit inputs and outputs.
396
+
397
+ Examples
398
+ --------
399
+
400
+ >>> from pyedb import Edb
401
+ >>> edbapp = Edb("myaedbfolder")
402
+ >>> edbapp.components.IOs
403
+
404
+ """
405
+ self._ios = {}
406
+ for el, val in self.instances.items():
407
+ if val.type == "IO":
408
+ self._ios[el] = val
409
+ return self._ios
410
+
411
+ @property
412
+ def Others(self):
413
+ """Other core components.
414
+
415
+ Returns
416
+ -------
417
+ dict[str, :class:`pyedb.dotnet.edb_core.edb_data.components_data.EDBComponent`]
418
+ Dictionary of other core components.
419
+
420
+ Examples
421
+ --------
422
+
423
+ >>> from pyedb import Edb
424
+ >>> edbapp = Edb("myaedbfolder")
425
+ >>> edbapp.components.others
426
+
427
+ """
428
+ self._others = {}
429
+ for el, val in self.instances.items():
430
+ if val.type == "Other":
431
+ self._others[el] = val
432
+ return self._others
433
+
434
+ @property
435
+ def components_by_partname(self):
436
+ """Components by part name.
437
+
438
+ Returns
439
+ -------
440
+ dict
441
+ Dictionary of components by part name.
442
+
443
+ Examples
444
+ --------
445
+
446
+ >>> from pyedb import Edb
447
+ >>> edbapp = Edb("myaedbfolder")
448
+ >>> edbapp.components.components_by_partname
449
+
450
+ """
451
+ self._comps_by_part = {}
452
+ for el, val in self.instances.items():
453
+ if val.partname in self._comps_by_part.keys():
454
+ self._comps_by_part[val.partname].append(val)
455
+ else:
456
+ self._comps_by_part[val.partname] = [val]
457
+ return self._comps_by_part
458
+
459
+ @pyedb_function_handler()
460
+ def get_component_by_name(self, name):
461
+ """Retrieve a component by name.
462
+
463
+ Parameters
464
+ ----------
465
+ name : str
466
+ Name of the component.
467
+
468
+ Returns
469
+ -------
470
+ bool
471
+ ``True`` when successful, ``False`` when failed.
472
+
473
+ """
474
+ edbcmp = self._pedb.edb_api.cell.hierarchy.component.FindByName(self._active_layout, name)
475
+ if edbcmp is not None:
476
+ return edbcmp
477
+ else:
478
+ pass
479
+
480
+ @pyedb_function_handler()
481
+ def get_components_from_nets(self, netlist=None):
482
+ """Retrieve components from a net list.
483
+
484
+ Parameters
485
+ ----------
486
+ netlist : str, optional
487
+ Name of the net list. The default is ``None``.
488
+
489
+ Returns
490
+ -------
491
+ list
492
+ List of components that belong to the signal nets.
493
+
494
+ """
495
+ cmp_list = []
496
+ if isinstance(netlist, str):
497
+ netlist = [netlist]
498
+ components = list(self.instances.keys())
499
+ for refdes in components:
500
+ cmpnets = self._cmp[refdes].nets
501
+ if set(cmpnets).intersection(set(netlist)):
502
+ cmp_list.append(refdes)
503
+ return cmp_list
504
+
505
+ @pyedb_function_handler()
506
+ def _get_edb_pin_from_pin_name(self, cmp, pin):
507
+ if not isinstance(cmp, self._pedb.edb_api.cell.hierarchy.component):
508
+ return False
509
+ if not isinstance(pin, str):
510
+ pin = pin.GetName()
511
+ pins = self.get_pin_from_component(component=cmp, pinName=pin)
512
+ if pins:
513
+ return pins[0]
514
+ return False
515
+
516
+ @pyedb_function_handler()
517
+ def get_component_placement_vector(
518
+ self,
519
+ mounted_component,
520
+ hosting_component,
521
+ mounted_component_pin1,
522
+ mounted_component_pin2,
523
+ hosting_component_pin1,
524
+ hosting_component_pin2,
525
+ flipped=False,
526
+ ):
527
+ """Get the placement vector between 2 components.
528
+
529
+ Parameters
530
+ ----------
531
+ mounted_component : `edb.cell.hierarchy._hierarchy.Component`
532
+ Mounted component name.
533
+ hosting_component : `edb.cell.hierarchy._hierarchy.Component`
534
+ Hosting component name.
535
+ mounted_component_pin1 : str
536
+ Mounted component Pin 1 name.
537
+ mounted_component_pin2 : str
538
+ Mounted component Pin 2 name.
539
+ hosting_component_pin1 : str
540
+ Hosted component Pin 1 name.
541
+ hosting_component_pin2 : str
542
+ Hosted component Pin 2 name.
543
+ flipped : bool, optional
544
+ Either if the mounted component will be flipped or not.
545
+
546
+ Returns
547
+ -------
548
+ tuple
549
+ Tuple of Vector offset, rotation and solder height.
550
+
551
+ Examples
552
+ --------
553
+ >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
554
+ >>> hosting_cmp = edb1.components.get_component_by_name("U100")
555
+ >>> mounted_cmp = edb2.components.get_component_by_name("BGA")
556
+ >>> vector, rotation, solder_ball_height = edb1.components.get_component_placement_vector(
557
+ ... mounted_component=mounted_cmp,
558
+ ... hosting_component=hosting_cmp,
559
+ ... mounted_component_pin1="A12",
560
+ ... mounted_component_pin2="A14",
561
+ ... hosting_component_pin1="A12",
562
+ ... hosting_component_pin2="A14")
563
+ """
564
+ m_pin1_pos = [0.0, 0.0]
565
+ m_pin2_pos = [0.0, 0.0]
566
+ h_pin1_pos = [0.0, 0.0]
567
+ h_pin2_pos = [0.0, 0.0]
568
+ if not isinstance(mounted_component, self._pedb.edb_api.cell.hierarchy.component):
569
+ return False
570
+ if not isinstance(hosting_component, self._pedb.edb_api.cell.hierarchy.component):
571
+ return False
572
+
573
+ if mounted_component_pin1:
574
+ m_pin1 = self._get_edb_pin_from_pin_name(mounted_component, mounted_component_pin1)
575
+ m_pin1_pos = self.get_pin_position(m_pin1)
576
+ if mounted_component_pin2:
577
+ m_pin2 = self._get_edb_pin_from_pin_name(mounted_component, mounted_component_pin2)
578
+ m_pin2_pos = self.get_pin_position(m_pin2)
579
+
580
+ if hosting_component_pin1:
581
+ h_pin1 = self._get_edb_pin_from_pin_name(hosting_component, hosting_component_pin1)
582
+ h_pin1_pos = self.get_pin_position(h_pin1)
583
+
584
+ if hosting_component_pin2:
585
+ h_pin2 = self._get_edb_pin_from_pin_name(hosting_component, hosting_component_pin2)
586
+ h_pin2_pos = self.get_pin_position(h_pin2)
587
+ #
588
+ vector = [h_pin1_pos[0] - m_pin1_pos[0], h_pin1_pos[1] - m_pin1_pos[1]]
589
+ vector1 = GeometryOperators.v_points(m_pin1_pos, m_pin2_pos)
590
+ vector2 = GeometryOperators.v_points(h_pin1_pos, h_pin2_pos)
591
+ multiplier = 1
592
+ if flipped:
593
+ multiplier = -1
594
+ vector1[1] = multiplier * vector1[1]
595
+
596
+ rotation = GeometryOperators.v_angle_sign_2D(vector1, vector2, False)
597
+ if rotation != 0.0:
598
+ layinst = mounted_component.GetLayout().GetLayoutInstance()
599
+ cmpinst = layinst.GetLayoutObjInstance(mounted_component, None)
600
+ center = cmpinst.GetCenter()
601
+ center_double = [center.X.ToDouble(), center.Y.ToDouble()]
602
+ vector_center = GeometryOperators.v_points(center_double, m_pin1_pos)
603
+ x_v2 = vector_center[0] * math.cos(rotation) + multiplier * vector_center[1] * math.sin(rotation)
604
+ y_v2 = -1 * vector_center[0] * math.sin(rotation) + multiplier * vector_center[1] * math.cos(rotation)
605
+ new_vector = [x_v2 + center_double[0], y_v2 + center_double[1]]
606
+ vector = [h_pin1_pos[0] - new_vector[0], h_pin1_pos[1] - new_vector[1]]
607
+
608
+ if vector:
609
+ solder_ball_height = self.get_solder_ball_height(mounted_component)
610
+ return True, vector, rotation, solder_ball_height
611
+ self._logger.warning("Failed to compute vector.")
612
+ return False, [0, 0], 0, 0
613
+
614
+ @pyedb_function_handler()
615
+ def get_solder_ball_height(self, cmp):
616
+ """Get component solder ball height.
617
+
618
+ Parameters
619
+ ----------
620
+ cmp : str or self._pedb.component
621
+ EDB component or str component name.
622
+
623
+ Returns
624
+ -------
625
+ double, bool
626
+ Salder ball height vale, ``False`` when failed.
627
+
628
+ """
629
+ if cmp is not None:
630
+ if not (isinstance(cmp, self._pedb.edb_api.cell.hierarchy.component)):
631
+ cmp = self.get_component_by_name(cmp)
632
+ cmp_prop = cmp.GetComponentProperty().Clone()
633
+ return cmp_prop.GetSolderBallProperty().GetHeight()
634
+ return False
635
+
636
+ @pyedb_function_handler()
637
+ def create_source_on_component(self, sources=None):
638
+ """Create voltage, current source, or resistor on component.
639
+
640
+ Parameters
641
+ ----------
642
+ sources : list[Source]
643
+ List of ``edb_data.sources.Source`` objects.
644
+
645
+ Returns
646
+ -------
647
+ bool
648
+ ``True`` when successful, ``False`` when failed.
649
+
650
+ """
651
+
652
+ if not sources: # pragma: no cover
653
+ return False
654
+ if isinstance(sources, Source): # pragma: no cover
655
+ sources = [sources]
656
+ if isinstance(sources, list): # pragma: no cover
657
+ for src in sources:
658
+ if not isinstance(src, Source): # pragma: no cover
659
+ self._logger.error("List of source objects must be passed as an argument.")
660
+ return False
661
+ for source in sources:
662
+ positive_pins = self.get_pin_from_component(source.positive_node.component, source.positive_node.net)
663
+ negative_pins = self.get_pin_from_component(source.negative_node.component, source.negative_node.net)
664
+ positive_pin_group = self.create_pingroup_from_pins(positive_pins)
665
+ if not positive_pin_group: # pragma: no cover
666
+ return False
667
+ negative_pin_group = self.create_pingroup_from_pins(negative_pins)
668
+ if not negative_pin_group: # pragma: no cover
669
+ return False
670
+ if source.source_type == SourceType.Vsource: # pragma: no cover
671
+ positive_pin_group_term = self._create_pin_group_terminal(
672
+ positive_pin_group,
673
+ )
674
+ negative_pin_group_term = self._create_pin_group_terminal(negative_pin_group, isref=True)
675
+ positive_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kVoltageSource)
676
+ negative_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kVoltageSource)
677
+ term_name = source.name
678
+ positive_pin_group_term.SetName(term_name)
679
+ negative_pin_group_term.SetName("{}_ref".format(term_name))
680
+ positive_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
681
+ negative_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
682
+ positive_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
683
+ negative_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
684
+ positive_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
685
+ negative_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
686
+ positive_pin_group_term.SetReferenceTerminal(negative_pin_group_term)
687
+ elif source.source_type == SourceType.Isource: # pragma: no cover
688
+ positive_pin_group_term = self._create_pin_group_terminal(
689
+ positive_pin_group,
690
+ )
691
+ negative_pin_group_term = self._create_pin_group_terminal(negative_pin_group, isref=True)
692
+ positive_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kCurrentSource)
693
+ negative_pin_group_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.kCurrentSource)
694
+ term_name = source.name
695
+ positive_pin_group_term.SetName(term_name)
696
+ negative_pin_group_term.SetName("{}_ref".format(term_name))
697
+ positive_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
698
+ negative_pin_group_term.SetSourceAmplitude(self._get_edb_value(source.amplitude))
699
+ positive_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
700
+ negative_pin_group_term.SetSourcePhase(self._get_edb_value(source.phase))
701
+ positive_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
702
+ negative_pin_group_term.SetImpedance(self._get_edb_value(source.impedance))
703
+ positive_pin_group_term.SetReferenceTerminal(negative_pin_group_term)
704
+ elif source.source_type == SourceType.Rlc: # pragma: no cover
705
+ self.create(
706
+ pins=[positive_pins[0], negative_pins[0]],
707
+ component_name=source.name,
708
+ is_rlc=True,
709
+ r_value=source.r_value,
710
+ l_value=source.l_value,
711
+ c_value=source.c_value,
712
+ )
713
+ return True
714
+
715
+ @pyedb_function_handler()
716
+ def create_port_on_pins(self, refdes, pins, reference_pins, impedance=50.0, port_name=None, pec_boundary=False):
717
+ """Create circuit port between pins and reference ones.
718
+
719
+ Parameters
720
+ ----------
721
+ refdes : Component reference designator
722
+ str or EDBComponent object.
723
+ pins : pin name where the terminal has to be created. Single pin or several ones can be provided.If several
724
+ pins are provided a pin group will is created. Pin names can be the EDB name or the EDBPadstackInstance one.
725
+ For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1`` or ``Pin1`` can be provided and
726
+ will be handled.
727
+ str, [str], EDBPadstackInstance, [EDBPadstackInstance]
728
+ reference_pins : reference pin name used for terminal reference. Single pin or several ones can be provided.
729
+ If several pins are provided a pin group will is created. Pin names can be the EDB name or the
730
+ EDBPadstackInstance one. For instance the pin called ``Pin1`` located on component ``U1``, ``U1-Pin1``
731
+ or ``Pin1`` can be provided and will be handled.
732
+ str, [str], EDBPadstackInstance, [EDBPadstackInstance]
733
+ impedance : Port impedance
734
+ str, float
735
+ port_name : str, optional
736
+ Port name. The default is ``None``, in which case a name is automatically assigned.
737
+ pec_boundary : bool, optional
738
+ Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
739
+ a perfect short is created between the pin and impedance is ignored. This
740
+ parameter is only supported on a port created between two pins, such as
741
+ when there is no pin group.
742
+
743
+ Returns
744
+ -------
745
+ EDB terminal created, or False if failed to create.
746
+
747
+ Example:
748
+ >>> from pyedb import Edb
749
+ >>> edb = Edb(path_to_edb_file)
750
+ >>> pin = "AJ6"
751
+ >>> ref_pins = ["AM7", "AM4"]
752
+ Or to take all reference pins
753
+ >>> ref_pins = [pin for pin in list(edb.components["U2A5"].pins.values()) if pin.net_name == "GND"]
754
+ >>> edb.components.create_port_on_pins(refdes="U2A5", pins=pin, reference_pins=ref_pins)
755
+ >>> edb.save_edb()
756
+ >>> edb.close_edb()
757
+ """
758
+
759
+ if isinstance(pins, str) or isinstance(pins, EDBPadstackInstance):
760
+ pins = [pins]
761
+ if isinstance(reference_pins, str):
762
+ reference_pins = [reference_pins]
763
+ if isinstance(refdes, str) or isinstance(refdes, EDBComponent):
764
+ refdes = self.instances[refdes]
765
+ if len([pin for pin in pins if isinstance(pin, str)]) == len(pins):
766
+ cmp_pins = []
767
+ for pin_name in pins:
768
+ cmp_pin = [pin for pin in list(refdes.pins.values()) if pin_name == pin.name]
769
+ if not cmp_pin:
770
+ cmp_pin = [pin for pin in list(refdes.pins.values()) if pin_name == pin.name.split("-")[1]]
771
+ if cmp_pin:
772
+ cmp_pins.append(cmp_pin[0])
773
+ if not cmp_pins:
774
+ return
775
+ pins = cmp_pins
776
+ if not len([pin for pin in pins if isinstance(pin, EDBPadstackInstance)]) == len(pins):
777
+ self._logger.error("Pin list must contain only pins instances")
778
+ return
779
+ if not port_name:
780
+ port_name = "Port_{}_{}".format(pins[0].net_name, pins[0].name)
781
+ if len([pin for pin in reference_pins if isinstance(pin, str)]) == len(reference_pins):
782
+ ref_cmp_pins = []
783
+ for ref_pin_name in reference_pins:
784
+ cmp_ref_pin = [pin for pin in list(refdes.pins.values()) if ref_pin_name == pin.name]
785
+ if not cmp_ref_pin:
786
+ cmp_ref_pin = [pin for pin in list(refdes.pins.values()) if ref_pin_name == pin.name.split("-")[1]]
787
+ if cmp_ref_pin:
788
+ ref_cmp_pins.append(cmp_ref_pin[0])
789
+ if not ref_cmp_pins:
790
+ return
791
+ reference_pins = ref_cmp_pins
792
+ if not len([pin for pin in reference_pins if isinstance(pin, EDBPadstackInstance)]) == len(reference_pins):
793
+ return
794
+ if len(pins) > 1:
795
+ pec_boundary = False
796
+ self._logger.info(
797
+ "Disabling PEC boundary creation, this feature is supported on single pin "
798
+ "ports only, {} pins found".format(len(pins))
799
+ )
800
+ group_name = "group_{}".format(port_name)
801
+ pin_group = self.create_pingroup_from_pins(pins, group_name)
802
+ term = self._create_pin_group_terminal(pingroup=pin_group, term_name=port_name)
803
+
804
+ else:
805
+ term = self._create_terminal(pins[0].primitive_object, term_name=port_name)
806
+ term.SetIsCircuitPort(True)
807
+ if len(reference_pins) > 1:
808
+ pec_boundary = False
809
+ self._logger.info(
810
+ "Disabling PEC boundary creation. This feature is supported on single pin"
811
+ "ports only {} reference pins found.".format(len(reference_pins))
812
+ )
813
+ ref_group_name = "group_{}_ref".format(port_name)
814
+ ref_pin_group = self.create_pingroup_from_pins(reference_pins, ref_group_name)
815
+ ref_term = self._create_pin_group_terminal(pingroup=ref_pin_group, term_name=port_name + "_ref")
816
+ else:
817
+ ref_term = self._create_terminal(reference_pins[0].primitive_object, term_name=port_name + "_ref")
818
+ ref_term.SetIsCircuitPort(True)
819
+ term.SetImpedance(self._edb.utility.value(impedance))
820
+ term.SetReferenceTerminal(ref_term)
821
+ if pec_boundary:
822
+ term.SetIsCircuitPort(False)
823
+ ref_term.SetIsCircuitPort(False)
824
+ term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.PecBoundary)
825
+ ref_term.SetBoundaryType(self._edb.cell.terminal.BoundaryType.PecBoundary)
826
+ self._logger.info(
827
+ "PEC boundary created between pin {} and reference pin {}".format(pins[0].name, reference_pins[0].name)
828
+ )
829
+ if term:
830
+ return term
831
+ return False
832
+
833
+ @pyedb_function_handler()
834
+ def create_port_on_component(
835
+ self,
836
+ component,
837
+ net_list,
838
+ port_type=SourceType.CoaxPort,
839
+ do_pingroup=True,
840
+ reference_net="gnd",
841
+ port_name=None,
842
+ solder_balls_height=None,
843
+ solder_balls_size=None,
844
+ solder_balls_mid_size=None,
845
+ ):
846
+ """Create ports on a component.
847
+
848
+ Parameters
849
+ ----------
850
+ component : str or self._pedb.component
851
+ EDB component or str component name.
852
+ net_list : str or list of string.
853
+ List of nets where ports must be created on the component.
854
+ If the net is not part of the component, this parameter is skipped.
855
+ port_type : SourceType enumerator, CoaxPort or CircuitPort
856
+ Type of port to create. ``CoaxPort`` generates solder balls.
857
+ ``CircuitPort`` generates circuit ports on pins belonging to the net list.
858
+ do_pingroup : bool
859
+ True activate pingroup during port creation (only used with combination of CoaxPort),
860
+ False will take the closest reference pin and generate one port per signal pin.
861
+ refnet : string or list of string.
862
+ list of the reference net.
863
+ port_name : str
864
+ Port name for overwriting the default port-naming convention,
865
+ which is ``[component][net][pin]``. The port name must be unique.
866
+ If a port with the specified name already exists, the
867
+ default naming convention is used so that port creation does
868
+ not fail.
869
+ solder_balls_height : float, optional
870
+ Solder balls height used for the component. When provided default value is overwritten and must be
871
+ provided in meter.
872
+ solder_balls_size : float, optional
873
+ Solder balls diameter. When provided auto evaluation based on padstack size will be disabled.
874
+ solder_balls_mid_size : float, optional
875
+ Solder balls mid diameter. When provided if value is different than solder balls size, spheroid shape will
876
+ be switched.
877
+
878
+ Returns
879
+ -------
880
+ double, bool
881
+ Salder ball height vale, ``False`` when failed.
882
+
883
+ Examples
884
+ --------
885
+
886
+ >>> from pyedb import Edb
887
+ >>> edbapp = Edb("myaedbfolder")
888
+ >>> net_list = ["M_DQ<1>", "M_DQ<2>", "M_DQ<3>", "M_DQ<4>", "M_DQ<5>"]
889
+ >>> edbapp.components.create_port_on_component(cmp="U2A5", net_list=net_list,
890
+ >>> port_type=SourceType.CoaxPort, do_pingroup=False, refnet="GND")
891
+
892
+ """
893
+ if isinstance(component, str):
894
+ component = self.instances[component].edbcomponent
895
+ if not isinstance(net_list, list):
896
+ net_list = [net_list]
897
+ for net in net_list:
898
+ if not isinstance(net, str):
899
+ try:
900
+ net_name = net.name
901
+ if net_name != "":
902
+ net_list.append(net_name)
903
+ except:
904
+ pass
905
+ if reference_net in net_list:
906
+ net_list.remove(reference_net)
907
+ cmp_pins = [
908
+ p for p in list(component.LayoutObjs) if int(p.GetObjType()) == 1 and p.GetNet().GetName() in net_list
909
+ ]
910
+ for p in cmp_pins: # pragma no cover
911
+ if not p.IsLayoutPin():
912
+ p.SetIsLayoutPin(True)
913
+ if len(cmp_pins) == 0:
914
+ self._logger.info(
915
+ "No pins found on component {}, searching padstack instances instead".format(component.GetName())
916
+ )
917
+ return False
918
+ pin_layers = cmp_pins[0].GetPadstackDef().GetData().GetLayerNames()
919
+ if port_type == SourceType.CoaxPort:
920
+ pad_params = self._padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
921
+ if not pad_params[0] == 7:
922
+ if not solder_balls_size: # pragma no cover
923
+ sball_diam = min([self._pedb.edb_value(val).ToDouble() for val in pad_params[1]])
924
+ sball_mid_diam = sball_diam
925
+ else: # pragma no cover
926
+ sball_diam = solder_balls_size
927
+ if solder_balls_mid_size:
928
+ sball_mid_diam = solder_balls_mid_size
929
+ else:
930
+ sball_mid_diam = solder_balls_size
931
+ if not solder_balls_height: # pragma no cover
932
+ solder_balls_height = 2 * sball_diam / 3
933
+ else: # pragma no cover
934
+ if not solder_balls_size:
935
+ bbox = pad_params[1]
936
+ sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
937
+ else:
938
+ sball_diam = solder_balls_size
939
+ if not solder_balls_height:
940
+ solder_balls_height = 2 * sball_diam / 3
941
+ if solder_balls_mid_size:
942
+ sball_mid_diam = solder_balls_mid_size
943
+ else:
944
+ sball_mid_diam = sball_diam
945
+ sball_shape = "Cylinder"
946
+ if not sball_diam == sball_mid_diam:
947
+ sball_shape = "Spheroid"
948
+ self.set_solder_ball(
949
+ component=component,
950
+ sball_height=solder_balls_height,
951
+ sball_diam=sball_diam,
952
+ sball_mid_diam=sball_mid_diam,
953
+ shape=sball_shape,
954
+ )
955
+ for pin in cmp_pins:
956
+ self._padstack.create_coax_port(padstackinstance=pin, name=port_name)
957
+
958
+ elif port_type == SourceType.CircPort: # pragma no cover
959
+ ref_pins = [
960
+ p
961
+ for p in list(component.LayoutObjs)
962
+ if int(p.GetObjType()) == 1 and p.GetNet().GetName() in reference_net
963
+ ]
964
+ for p in ref_pins:
965
+ if not p.IsLayoutPin():
966
+ p.SetIsLayoutPin(True)
967
+ if len(ref_pins) == 0:
968
+ self._logger.info("No reference pin found on component {}.".format(component.GetName()))
969
+ if do_pingroup:
970
+ if len(ref_pins) == 1:
971
+ ref_pin_group_term = self._create_terminal(ref_pins[0])
972
+ else:
973
+ ref_pin_group = self.create_pingroup_from_pins(ref_pins)
974
+ if not ref_pin_group:
975
+ return False
976
+ ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=True)
977
+ if not ref_pin_group_term:
978
+ return False
979
+ for net in net_list:
980
+ pins = [pin for pin in cmp_pins if pin.GetNet().GetName() == net]
981
+ if pins:
982
+ if len(pins) == 1:
983
+ pin_term = self._create_terminal(pins[0])
984
+ if pin_term:
985
+ pin_term.SetReferenceTerminal(ref_pin_group_term)
986
+ else:
987
+ pin_group = self.create_pingroup_from_pins(pins)
988
+ if not pin_group:
989
+ return False
990
+ pin_group_term = self._create_pin_group_terminal(pin_group)
991
+ if pin_group_term:
992
+ pin_group_term.SetReferenceTerminal(ref_pin_group_term)
993
+ else:
994
+ self._logger.info("No pins found on component {} for the net {}".format(component, net))
995
+ else:
996
+ ref_pin_group = self.create_pingroup_from_pins(ref_pins)
997
+ if not ref_pin_group:
998
+ self._logger.warning("failed to create reference pin group")
999
+ return False
1000
+ ref_pin_group_term = self._create_pin_group_terminal(ref_pin_group, isref=True)
1001
+ for net in net_list:
1002
+ pins = [pin for pin in cmp_pins if pin.GetNet().GetName() == net]
1003
+ for pin in pins:
1004
+ pin_group = self.create_pingroup_from_pins([pin])
1005
+ pin_group_term = self._create_pin_group_terminal(pin_group, isref=False)
1006
+ pin_group_term.SetReferenceTerminal(ref_pin_group_term)
1007
+ return True
1008
+
1009
+ @pyedb_function_handler()
1010
+ def _create_terminal(self, pin, term_name=None):
1011
+ """Create terminal on component pin.
1012
+
1013
+ Parameters
1014
+ ----------
1015
+ pin : Edb padstack instance.
1016
+
1017
+ term_name : Terminal name (Optional).
1018
+ str.
1019
+
1020
+ Returns
1021
+ -------
1022
+ EDB terminal.
1023
+ """
1024
+
1025
+ res, from_layer, _ = pin.GetLayerRange()
1026
+ cmp_name = pin.GetComponent().GetName()
1027
+ net_name = pin.GetNet().GetName()
1028
+ pin_name = pin.GetName()
1029
+ if term_name is None:
1030
+ term_name = "{}.{}.{}".format(cmp_name, pin_name, net_name)
1031
+ for term in list(self._pedb.active_layout.Terminals):
1032
+ if term.GetName() == term_name:
1033
+ return term
1034
+ term = self._edb.cell.terminal.PadstackInstanceTerminal.Create(
1035
+ pin.GetLayout(), pin.GetNet(), term_name, pin, from_layer
1036
+ )
1037
+ return term
1038
+
1039
+ @pyedb_function_handler()
1040
+ def _get_closest_pin_from(self, pin, ref_pinlist):
1041
+ """Returns the closest pin from given pin among the list of reference pins.
1042
+
1043
+ Parameters
1044
+ ----------
1045
+ pin : Edb padstack instance.
1046
+
1047
+ ref_pinlist : list of reference edb pins.
1048
+
1049
+ Returns
1050
+ -------
1051
+ Edb pin.
1052
+
1053
+ """
1054
+ res, pin_position, pin_rot = pin.GetPositionAndRotation(
1055
+ self._pedb.point_data(0.0, 0.0),
1056
+ 0.0,
1057
+ )
1058
+ distance = 1e3
1059
+ closest_pin = ref_pinlist[0]
1060
+ for ref_pin in ref_pinlist:
1061
+ res, ref_pin_position, ref_pin_rot = ref_pin.GetPositionAndRotation(
1062
+ self._pedb.point_data(0.0, 0.0),
1063
+ 0.0,
1064
+ )
1065
+ temp_distance = pin_position.Distance(ref_pin_position)
1066
+ if temp_distance < distance:
1067
+ distance = temp_distance
1068
+ closest_pin = ref_pin
1069
+ return closest_pin
1070
+
1071
+ @pyedb_function_handler()
1072
+ def replace_rlc_by_gap_boundaries(self, component=None):
1073
+ """Replace RLC component by RLC gap boundaries. These boundary types are compatible with 3D modeler export.
1074
+ Only 2 pins RLC components are supported in this command.
1075
+
1076
+ Parameters
1077
+ ----------
1078
+ component : str
1079
+ Reference designator of the RLC component.
1080
+
1081
+ Returns
1082
+ -------
1083
+ bool
1084
+ ``True`` when succeed, ``False`` if it failed.
1085
+
1086
+ Examples
1087
+ --------
1088
+ >>> from pyedb import Edb
1089
+ >>> edb = Edb(edb_file)
1090
+ >>> for refdes, cmp in edb.components.capacitors.items():
1091
+ >>> edb.components.replace_rlc_by_gap_boundaries(refdes)
1092
+ >>> edb.save_edb()
1093
+ >>> edb.close_edb()
1094
+ """
1095
+ if not component: # pragma no cover
1096
+ return False
1097
+ if isinstance(component, str):
1098
+ component = self.instances[component]
1099
+ if not component: # pragma no cover
1100
+ self._logger.error("component %s not found.", component)
1101
+ return False
1102
+ component_type = component.edbcomponent.GetComponentType()
1103
+ if (
1104
+ component_type == self._edb.definition.ComponentType.Other
1105
+ or component_type == self._edb.definition.ComponentType.IC
1106
+ or component_type == self._edb.definition.ComponentType.IO
1107
+ ):
1108
+ self._logger.info("Component %s passed to deactivate is not an RLC.", component.refdes)
1109
+ return False
1110
+ component.is_enabled = False
1111
+ return self.add_rlc_boundary(component.refdes, False)
1112
+
1113
+ @pyedb_function_handler()
1114
+ def deactivate_rlc_component(self, component=None, create_circuit_port=False, pec_boundary=False):
1115
+ """Deactivate RLC component with a possibility to convert it to a circuit port.
1116
+
1117
+ Parameters
1118
+ ----------
1119
+ component : str
1120
+ Reference designator of the RLC component.
1121
+
1122
+ create_circuit_port : bool, optional
1123
+ Whether to replace the deactivated RLC component with a circuit port. The default
1124
+ is ``False``.
1125
+ pec_boundary : bool, optional
1126
+ Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
1127
+ a perfect short is created between the pin and impedance is ignored. This
1128
+ parameter is only supported on a port created between two pins, such as
1129
+ when there is no pin group.
1130
+
1131
+ Returns
1132
+ -------
1133
+ bool
1134
+ ``True`` when successful, ``False`` when failed.
1135
+
1136
+ Examples
1137
+ --------
1138
+ >>> from pyedb import Edb
1139
+ >>> edb_file = r'C:\my_edb_file.aedb'
1140
+ >>> edb = Edb(edb_file)
1141
+ >>> for cmp in list(edb.components.instances.keys()):
1142
+ >>> edb.components.deactivate_rlc_component(component=cmp, create_circuit_port=False)
1143
+ >>> edb.save_edb()
1144
+ >>> edb.close_edb()
1145
+ """
1146
+ if not component:
1147
+ return False
1148
+ if isinstance(component, str):
1149
+ component = self.instances[component]
1150
+ if not component:
1151
+ self._logger.error("component %s not found.", component)
1152
+ return False
1153
+ component_type = component.edbcomponent.GetComponentType()
1154
+ if (
1155
+ component_type == self._edb.definition.ComponentType.Other
1156
+ or component_type == self._edb.definition.ComponentType.IC
1157
+ or component_type == self._edb.definition.ComponentType.IO
1158
+ ):
1159
+ self._logger.info("Component %s passed to deactivate is not an RLC.", component.refdes)
1160
+ return False
1161
+ component.is_enabled = False
1162
+ return self.add_port_on_rlc_component(
1163
+ component=component.refdes, circuit_ports=create_circuit_port, pec_boundary=pec_boundary
1164
+ )
1165
+
1166
+ @pyedb_function_handler()
1167
+ def add_port_on_rlc_component(self, component=None, circuit_ports=True, pec_boundary=False):
1168
+ """Deactivate RLC component and replace it with a circuit port.
1169
+ The circuit port supports only two-pin components.
1170
+
1171
+ Parameters
1172
+ ----------
1173
+ component : str
1174
+ Reference designator of the RLC component.
1175
+
1176
+ circuit_ports : bool
1177
+ ``True`` will replace RLC component by circuit ports, ``False`` gap ports compatible with HFSS 3D modeler
1178
+ export.
1179
+
1180
+ pec_boundary : bool, optional
1181
+ Whether to define the PEC boundary, The default is ``False``. If set to ``True``,
1182
+ a perfect short is created between the pin and impedance is ignored. This
1183
+ parameter is only supported on a port created between two pins, such as
1184
+ when there is no pin group.
1185
+
1186
+ Returns
1187
+ -------
1188
+ bool
1189
+ ``True`` when successful, ``False`` when failed.
1190
+ """
1191
+ if isinstance(component, str): # pragma: no cover
1192
+ component = self.instances[component]
1193
+ if not isinstance(component, EDBComponent): # pragma: no cover
1194
+ return False
1195
+ self.set_component_rlc(component.refdes)
1196
+ pins = self.get_pin_from_component(component.refdes)
1197
+ if len(pins) == 2: # pragma: no cover
1198
+ pin_layers = self._padstack._get_pin_layer_range(pins[0])
1199
+ pos_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
1200
+ self._active_layout,
1201
+ pins[0].GetNet(),
1202
+ "{}_{}".format(component.refdes, pins[0].GetName()),
1203
+ pins[0],
1204
+ pin_layers[0],
1205
+ False,
1206
+ )
1207
+ if not pos_pin_term: # pragma: no cover
1208
+ return False
1209
+ neg_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
1210
+ self._active_layout,
1211
+ pins[1].GetNet(),
1212
+ "{}_{}_ref".format(component.refdes, pins[1].GetName()),
1213
+ pins[1],
1214
+ pin_layers[0],
1215
+ False,
1216
+ )
1217
+ if not neg_pin_term: # pragma: no cover
1218
+ return False
1219
+ if pec_boundary:
1220
+ pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PecBoundary)
1221
+ neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PecBoundary)
1222
+ else:
1223
+ pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PortBoundary)
1224
+ neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.PortBoundary)
1225
+ pos_pin_term.SetName(component.refdes)
1226
+ pos_pin_term.SetReferenceTerminal(neg_pin_term)
1227
+ if circuit_ports and not pec_boundary:
1228
+ pos_pin_term.SetIsCircuitPort(True)
1229
+ neg_pin_term.SetIsCircuitPort(True)
1230
+ elif pec_boundary:
1231
+ pos_pin_term.SetIsCircuitPort(False)
1232
+ neg_pin_term.SetIsCircuitPort(False)
1233
+ else:
1234
+ pos_pin_term.SetIsCircuitPort(False)
1235
+ neg_pin_term.SetIsCircuitPort(False)
1236
+ self._logger.info("Component {} has been replaced by port".format(component.refdes))
1237
+ return True
1238
+ return False
1239
+
1240
+ @pyedb_function_handler()
1241
+ def add_rlc_boundary(self, component=None, circuit_type=True):
1242
+ """Add RLC gap boundary on component and replace it with a circuit port.
1243
+ The circuit port supports only 2-pin components.
1244
+
1245
+ Parameters
1246
+ ----------
1247
+ component : str
1248
+ Reference designator of the RLC component.
1249
+ circuit_type : bool
1250
+ When ``True`` circuit type are defined, if ``False`` gap type will be used instead (compatible with HFSS 3D
1251
+ modeler). Default value is ``True``.
1252
+
1253
+ Returns
1254
+ -------
1255
+ bool
1256
+ ``True`` when successful, ``False`` when failed.
1257
+ """
1258
+ if isinstance(component, str): # pragma: no cover
1259
+ component = self.instances[component]
1260
+ if not isinstance(component, EDBComponent): # pragma: no cover
1261
+ return False
1262
+ self.set_component_rlc(component.refdes)
1263
+ pins = self.get_pin_from_component(component.refdes)
1264
+ if len(pins) == 2: # pragma: no cover
1265
+ pin_layer = self._padstack._get_pin_layer_range(pins[0])[0]
1266
+ pos_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
1267
+ self._active_layout,
1268
+ pins[0].GetNet(),
1269
+ "{}_{}".format(component.refdes, pins[0].GetName()),
1270
+ pins[0],
1271
+ pin_layer,
1272
+ False,
1273
+ )
1274
+ if not pos_pin_term: # pragma: no cover
1275
+ return False
1276
+ neg_pin_term = self._pedb.edb_api.cell.terminal.PadstackInstanceTerminal.Create(
1277
+ self._active_layout,
1278
+ pins[1].GetNet(),
1279
+ "{}_{}_ref".format(component.refdes, pins[1].GetName()),
1280
+ pins[1],
1281
+ pin_layer,
1282
+ True,
1283
+ )
1284
+ if not neg_pin_term: # pragma: no cover
1285
+ return False
1286
+ pos_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.RlcBoundary)
1287
+ if not circuit_type:
1288
+ pos_pin_term.SetIsCircuitPort(False)
1289
+ else:
1290
+ pos_pin_term.SetIsCircuitPort(True)
1291
+ pos_pin_term.SetName(component.refdes)
1292
+ neg_pin_term.SetBoundaryType(self._pedb.edb_api.cell.terminal.BoundaryType.RlcBoundary)
1293
+ if not circuit_type:
1294
+ neg_pin_term.SetIsCircuitPort(False)
1295
+ else:
1296
+ neg_pin_term.SetIsCircuitPort(True)
1297
+ pos_pin_term.SetReferenceTerminal(neg_pin_term)
1298
+ rlc_values = component.rlc_values
1299
+ rlc = self._edb.utility.Rlc()
1300
+ if rlc_values[0]:
1301
+ rlc.REnabled = True
1302
+ rlc.R = self._edb.utility.value(rlc_values[0])
1303
+ if rlc_values[1]:
1304
+ rlc.LEnabled = True
1305
+ rlc.L = self._edb.utility.value(rlc_values[1])
1306
+ if rlc_values[2]:
1307
+ rlc.CEnabled = True
1308
+ rlc.C = self._edb.utility.value(rlc_values[2])
1309
+ rlc.is_parallel = component.is_parallel_rlc
1310
+ pos_pin_term.SetRlcBoundaryParameters(rlc)
1311
+ self._logger.info("Component {} has been replaced by port".format(component.refdes))
1312
+ return True
1313
+
1314
+ @pyedb_function_handler()
1315
+ def _create_pin_group_terminal(self, pingroup, isref=False, term_name=None):
1316
+ """Creates an EDB pin group terminal from a given EDB pin group.
1317
+
1318
+ Parameters
1319
+ ----------
1320
+ pingroup : Edb pin group.
1321
+
1322
+ isref : bool
1323
+
1324
+ term_name : Terminal name (Optional). If not provided default name is Component name, Pin name, Net name.
1325
+ str.
1326
+
1327
+ Returns
1328
+ -------
1329
+ Edb pin group terminal.
1330
+ """
1331
+ pin = list(pingroup.GetPins())[0]
1332
+ if term_name is None:
1333
+ term_name = "{}.{}.{}".format(pin.GetComponent().GetName(), pin.GetName(), pin.GetNet().GetName())
1334
+ for t in list(self._pedb.active_layout.Terminals):
1335
+ if t.GetName() == term_name:
1336
+ return t
1337
+ pingroup_term = self._edb.cell.terminal.PinGroupTerminal.Create(
1338
+ self._active_layout, pingroup.GetNet(), term_name, pingroup, isref
1339
+ )
1340
+ return pingroup_term
1341
+
1342
+ @pyedb_function_handler()
1343
+ def _is_top_component(self, cmp):
1344
+ """Test the component placement layer.
1345
+
1346
+ Parameters
1347
+ ----------
1348
+ cmp : self._pedb.component
1349
+ Edb component.
1350
+
1351
+ Returns
1352
+ -------
1353
+ bool
1354
+ ``True`` when component placed on top layer, ``False`` on bottom layer.
1355
+
1356
+
1357
+ """
1358
+ signal_layers = cmp.GetLayout().GetLayerCollection().Layers(self._edb.cell.layer_type_set.SignalLayerSet)
1359
+ if cmp.GetPlacementLayer() == signal_layers[0]:
1360
+ return True
1361
+ else:
1362
+ return False
1363
+
1364
+ @pyedb_function_handler()
1365
+ def _getComponentDefinition(self, name, pins):
1366
+ componentDefinition = self._pedb.edb_api.definition.ComponentDef.FindByName(self._db, name)
1367
+ if componentDefinition.IsNull():
1368
+ componentDefinition = self._pedb.edb_api.definition.ComponentDef.Create(self._db, name, None)
1369
+ if componentDefinition.IsNull():
1370
+ self._logger.error("Failed to create component definition {}".format(name))
1371
+ return None
1372
+ ind = 1
1373
+ for pin in pins:
1374
+ if not pin.GetName():
1375
+ pin.SetName(str(ind))
1376
+ ind += 1
1377
+ componentDefinitionPin = self._pedb.edb_api.definition.ComponentDefPin.Create(
1378
+ componentDefinition, pin.GetName()
1379
+ )
1380
+ if componentDefinitionPin.IsNull():
1381
+ self._logger.error("Failed to create component definition pin {}-{}".format(name, pin.GetName()))
1382
+ return None
1383
+ else:
1384
+ self._logger.warning("Found existing component definition for footprint {}".format(name))
1385
+ return componentDefinition
1386
+
1387
+ @pyedb_function_handler()
1388
+ def create_rlc_component(
1389
+ self, pins, component_name="", r_value=1.0, c_value=1e-9, l_value=1e-9, is_parallel=False
1390
+ ): # pragma: no cover
1391
+ """Create physical Rlc component.
1392
+
1393
+ Parameters
1394
+ ----------
1395
+ pins : list
1396
+ List of EDB pins, length must be 2, since only 2 pins component are currently supported.
1397
+ It can be an `dotnet.edb_core.edb_data.padstacks_data.EDBPadstackInstance` object or
1398
+ an Edb Padstack Instance object.
1399
+ component_name : str
1400
+ Component definition name.
1401
+ r_value : float
1402
+ Resistor value.
1403
+ c_value : float
1404
+ Capacitance value.
1405
+ l_value : float
1406
+ Inductor value.
1407
+ is_parallel : bool
1408
+ Using parallel model when ``True``, series when ``False``.
1409
+
1410
+ Returns
1411
+ -------
1412
+ Component
1413
+ Created EDB component.
1414
+
1415
+ """
1416
+ warnings.warn("`create_rlc_component` is deprecated. Use `create` method instead.", DeprecationWarning)
1417
+ return self.create(
1418
+ pins=pins,
1419
+ component_name=component_name,
1420
+ is_rlc=True,
1421
+ r_value=r_value,
1422
+ l_value=l_value,
1423
+ c_value=c_value,
1424
+ is_parallel=is_parallel,
1425
+ )
1426
+
1427
+ @pyedb_function_handler()
1428
+ def create(
1429
+ self,
1430
+ pins,
1431
+ component_name,
1432
+ placement_layer=None,
1433
+ component_part_name=None,
1434
+ is_rlc=False,
1435
+ r_value=0,
1436
+ c_value=0,
1437
+ l_value=0,
1438
+ is_parallel=False,
1439
+ ):
1440
+ """Create a component from pins.
1441
+
1442
+ Parameters
1443
+ ----------
1444
+ pins : list
1445
+ List of EDB core pins.
1446
+ component_name : str
1447
+ Name of the reference designator for the component.
1448
+ placement_layer : str, optional
1449
+ Name of the layer used for placing the component.
1450
+ component_part_name : str, optional
1451
+ Part name of the component.
1452
+ is_rlc : bool, optional
1453
+ Whether if the new component will be an RLC or not.
1454
+ r_value : float
1455
+ Resistor value.
1456
+ c_value : float
1457
+ Capacitance value.
1458
+ l_value : float
1459
+ Inductor value.
1460
+ is_parallel : bool
1461
+ Using parallel model when ``True``, series when ``False``.
1462
+
1463
+ Returns
1464
+ -------
1465
+ bool
1466
+ ``True`` when successful, ``False`` when failed.
1467
+
1468
+ Examples
1469
+ --------
1470
+
1471
+ >>> from pyedb import Edb
1472
+ >>> edbapp = Edb("myaedbfolder")
1473
+ >>> pins = edbapp.components.get_pin_from_component("A1")
1474
+ >>> edbapp.components.create(pins, "A1New")
1475
+
1476
+ """
1477
+ if component_part_name:
1478
+ compdef = self._getComponentDefinition(component_part_name, pins)
1479
+ else:
1480
+ compdef = self._getComponentDefinition(component_name, pins)
1481
+ if not compdef:
1482
+ return False
1483
+ new_cmp = self._pedb.edb_api.cell.hierarchy.component.Create(
1484
+ self._active_layout, component_name, compdef.GetName()
1485
+ )
1486
+
1487
+ if isinstance(pins[0], EDBPadstackInstance):
1488
+ pins = [i._edb_padstackinstance for i in pins]
1489
+ hosting_component_location = pins[0].GetComponent().GetTransform()
1490
+ for pin in pins:
1491
+ pin.SetIsLayoutPin(True)
1492
+ new_cmp.AddMember(pin)
1493
+ new_cmp.SetComponentType(self._edb.definition.ComponentType.Other)
1494
+ if not placement_layer:
1495
+ new_cmp_layer_name = pins[0].GetPadstackDef().GetData().GetLayerNames()[0]
1496
+ else:
1497
+ new_cmp_layer_name = placement_layer
1498
+ new_cmp_placement_layer = self._edb.cell.layer.FindByName(self._layout.layer_collection, new_cmp_layer_name)
1499
+ new_cmp.SetPlacementLayer(new_cmp_placement_layer)
1500
+
1501
+ if is_rlc and len(pins) == 2:
1502
+ rlc = self._edb.utility.utility.Rlc()
1503
+ rlc.IsParallel = is_parallel
1504
+ if r_value:
1505
+ rlc.REnabled = True
1506
+ rlc.R = self._get_edb_value(r_value)
1507
+ else:
1508
+ rlc.REnabled = False
1509
+ if l_value:
1510
+ rlc.LEnabled = True
1511
+ rlc.L = self._get_edb_value(l_value)
1512
+ else:
1513
+ rlc.LEnabled = False
1514
+ if c_value:
1515
+ rlc.CEnabled = True
1516
+ rlc.C = self._get_edb_value(c_value)
1517
+ else:
1518
+ rlc.CEnabled = False
1519
+ if rlc.REnabled and not rlc.CEnabled and not rlc.CEnabled:
1520
+ new_cmp.SetComponentType(self._edb.definition.ComponentType.Resistor)
1521
+ elif rlc.CEnabled and not rlc.REnabled and not rlc.LEnabled:
1522
+ new_cmp.SetComponentType(self._edb.definition.ComponentType.Capacitor)
1523
+ elif rlc.LEnabled and not rlc.REnabled and not rlc.CEnabled:
1524
+ new_cmp.SetComponentType(self._edb.definition.ComponentType.Inductor)
1525
+ else:
1526
+ new_cmp.SetComponentType(self._edb.definition.ComponentType.Resistor)
1527
+
1528
+ pin_pair = self._edb.utility.utility.PinPair(pins[0].GetName(), pins[1].GetName())
1529
+ rlc_model = self._edb.cell.hierarchy._hierarchy.PinPairModel()
1530
+ rlc_model.SetPinPairRlc(pin_pair, rlc)
1531
+ edb_rlc_component_property = self._edb.cell.hierarchy._hierarchy.RLCComponentProperty()
1532
+ if not edb_rlc_component_property.SetModel(rlc_model) or not new_cmp.SetComponentProperty(
1533
+ edb_rlc_component_property
1534
+ ):
1535
+ return False # pragma no cover
1536
+ new_cmp.SetTransform(hosting_component_location)
1537
+ new_edb_comp = EDBComponent(self._pedb, new_cmp)
1538
+ self._cmp[new_cmp.GetName()] = new_edb_comp
1539
+ return new_edb_comp
1540
+
1541
+ @pyedb_function_handler()
1542
+ def create_component_from_pins(
1543
+ self, pins, component_name, placement_layer=None, component_part_name=None
1544
+ ): # pragma: no cover
1545
+ """Create a component from pins.
1546
+
1547
+ .. deprecated:: 0.6.62
1548
+ Use :func:`create` method instead.
1549
+
1550
+ Parameters
1551
+ ----------
1552
+ pins : list
1553
+ List of EDB core pins.
1554
+ component_name : str
1555
+ Name of the reference designator for the component.
1556
+ placement_layer : str, optional
1557
+ Name of the layer used for placing the component.
1558
+ component_part_name : str, optional
1559
+ Part name of the component. It's created a new definition if doesn't exists.
1560
+
1561
+ Returns
1562
+ -------
1563
+ bool
1564
+ ``True`` when successful, ``False`` when failed.
1565
+
1566
+ Examples
1567
+ --------
1568
+
1569
+ >>> from pyedb import Edb
1570
+ >>> edbapp = Edb("myaedbfolder")
1571
+ >>> pins = edbapp.components.get_pin_from_component("A1")
1572
+ >>> edbapp.components.create(pins, "A1New")
1573
+
1574
+ """
1575
+ warnings.warn("`create_component_from_pins` is deprecated. Use `create` method instead.", DeprecationWarning)
1576
+ return self.create(
1577
+ pins=pins,
1578
+ component_name=component_name,
1579
+ placement_layer=placement_layer,
1580
+ component_part_name=component_part_name,
1581
+ is_rlc=False,
1582
+ )
1583
+
1584
+ @pyedb_function_handler()
1585
+ def set_component_model(self, componentname, model_type="Spice", modelpath=None, modelname=None):
1586
+ """Assign a Spice or Touchstone model to a component.
1587
+
1588
+ Parameters
1589
+ ----------
1590
+ componentname : str
1591
+ Name of the component.
1592
+ model_type : str, optional
1593
+ Type of the model. Options are ``"Spice"`` and
1594
+ ``"Touchstone"``. The default is ``"Spice"``.
1595
+ modelpath : str, optional
1596
+ Full path to the model file. The default is ``None``.
1597
+ modelname : str, optional
1598
+ Name of the model. The default is ``None``.
1599
+
1600
+ Returns
1601
+ -------
1602
+ bool
1603
+ ``True`` when successful, ``False`` when failed.
1604
+
1605
+ Examples
1606
+ --------
1607
+
1608
+ >>> from pyedb import Edb
1609
+ >>> edbapp = Edb("myaedbfolder")
1610
+ >>> edbapp.components.set_component_model("A1", model_type="Spice",
1611
+ ... modelpath="pathtospfile",
1612
+ ... modelname="spicemodelname")
1613
+
1614
+ """
1615
+ if not modelname:
1616
+ modelname = get_filename_without_extension(modelpath)
1617
+ edbComponent = self.get_component_by_name(componentname)
1618
+ if str(edbComponent.EDBHandle) == "0":
1619
+ return False
1620
+ edbRlcComponentProperty = edbComponent.GetComponentProperty().Clone()
1621
+
1622
+ componentPins = self.get_pin_from_component(componentname)
1623
+ componentNets = self.get_nets_from_pin_list(componentPins)
1624
+ pinNumber = len(componentPins)
1625
+ if model_type == "Spice":
1626
+ with open(modelpath, "r") as f:
1627
+ for line in f:
1628
+ if "subckt" in line.lower():
1629
+ pinNames = [i.strip() for i in re.split(" |\t", line) if i]
1630
+ pinNames.remove(pinNames[0])
1631
+ pinNames.remove(pinNames[0])
1632
+ break
1633
+ if len(pinNames) == pinNumber:
1634
+ spiceMod = self._edb.cell.hierarchy._hierarchy.SPICEModel()
1635
+ spiceMod.SetModelPath(modelpath)
1636
+ spiceMod.SetModelName(modelname)
1637
+ terminal = 1
1638
+ for pn in pinNames:
1639
+ spiceMod.AddTerminalPinPair(pn, str(terminal))
1640
+ terminal += 1
1641
+
1642
+ edbRlcComponentProperty.SetModel(spiceMod)
1643
+ if not edbComponent.SetComponentProperty(edbRlcComponentProperty):
1644
+ self._logger.error("Error assigning the `Spice` model.")
1645
+ return False
1646
+ else:
1647
+ self._logger.error("Wrong number of Pins")
1648
+ return False
1649
+
1650
+ elif model_type == "Touchstone": # pragma: no cover
1651
+ nPortModelName = modelname
1652
+ edbComponentDef = edbComponent.GetComponentDef()
1653
+ nPortModel = self._edb.definition.NPortComponentModel.FindByName(edbComponentDef, nPortModelName)
1654
+ if nPortModel.IsNull():
1655
+ nPortModel = self._edb.definition.NPortComponentModel.Create(nPortModelName)
1656
+ nPortModel.SetReferenceFile(modelpath)
1657
+ edbComponentDef.AddComponentModel(nPortModel)
1658
+
1659
+ sParameterMod = self._edb.cell.hierarchy._hierarchy.SParameterModel()
1660
+ sParameterMod.SetComponentModelName(nPortModelName)
1661
+ gndnets = filter(lambda x: "gnd" in x.lower(), componentNets)
1662
+ if len(list(gndnets)) > 0: # pragma: no cover
1663
+ net = gndnets[0]
1664
+ else: # pragma: no cover
1665
+ net = componentNets[len(componentNets) - 1]
1666
+ sParameterMod.SetReferenceNet(net)
1667
+ edbRlcComponentProperty.SetModel(sParameterMod)
1668
+ if not edbComponent.SetComponentProperty(edbRlcComponentProperty):
1669
+ self._logger.error("Error assigning the `Touchstone` model")
1670
+ return False
1671
+ return True
1672
+
1673
+ @pyedb_function_handler()
1674
+ def create_pingroup_from_pins(self, pins, group_name=None):
1675
+ """Create a pin group on a component.
1676
+
1677
+ Parameters
1678
+ ----------
1679
+ pins : list
1680
+ List of EDB pins.
1681
+ group_name : str, optional
1682
+ Name for the group. The default is ``None``, in which case
1683
+ a default name is assigned as follows: ``[component Name] [NetName]``.
1684
+
1685
+ Returns
1686
+ -------
1687
+ tuple
1688
+ The tuple is structured as: (bool, pingroup).
1689
+
1690
+ Examples
1691
+ --------
1692
+ >>> from pyedb import Edb
1693
+ >>> edbapp = Edb("myaedbfolder")
1694
+ >>> edbapp.components.create_pingroup_from_pins(gndpinlist, "MyGNDPingroup")
1695
+
1696
+ """
1697
+ if len(pins) < 1:
1698
+ self._logger.error("No pins specified for pin group %s", group_name)
1699
+ return (False, None)
1700
+ if len([pin for pin in pins if isinstance(pin, EDBPadstackInstance)]):
1701
+ _pins = [pin._edb_padstackinstance for pin in pins]
1702
+ if _pins:
1703
+ pins = _pins
1704
+ if group_name is None:
1705
+ group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(self._active_layout)
1706
+ for pin in pins:
1707
+ pin.SetIsLayoutPin(True)
1708
+ forbiden_car = "-><"
1709
+ group_name = group_name.translate({ord(i): "_" for i in forbiden_car})
1710
+ for pgroup in list(self._pedb.active_layout.PinGroups):
1711
+ if pgroup.GetName() == group_name:
1712
+ pin_group_exists = True
1713
+ if len(pgroup.GetPins()) == len(pins):
1714
+ pnames = [i.GetName() for i in pins]
1715
+ for p in pgroup.GetPins():
1716
+ if p.GetName() in pnames:
1717
+ continue
1718
+ else:
1719
+ group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(
1720
+ self._active_layout, group_name
1721
+ )
1722
+ pin_group_exists = False
1723
+ else:
1724
+ group_name = self._edb.cell.hierarchy.pin_group.GetUniqueName(self._active_layout, group_name)
1725
+ pin_group_exists = False
1726
+ if pin_group_exists:
1727
+ return pgroup
1728
+ pingroup = _retry_ntimes(
1729
+ 10,
1730
+ self._edb.cell.hierarchy.pin_group.Create,
1731
+ self._active_layout,
1732
+ group_name,
1733
+ convert_py_list_to_net_list(pins),
1734
+ )
1735
+ if pingroup.IsNull():
1736
+ return False
1737
+ else:
1738
+ pingroup.SetNet(pins[0].GetNet())
1739
+ return pingroup
1740
+
1741
+ @pyedb_function_handler()
1742
+ def delete_single_pin_rlc(self, deactivate_only=False):
1743
+ # type: (bool) -> list
1744
+ """Delete all RLC components with a single pin.
1745
+ Single pin component model type will be reverted to ``"RLC"``.
1746
+
1747
+ Parameters
1748
+ ----------
1749
+ deactivate_only : bool, optional
1750
+ Whether to only deactivate RLC components with a single point rather than
1751
+ delete them. The default is ``False``, in which case they are deleted.
1752
+
1753
+ Returns
1754
+ -------
1755
+ list
1756
+ List of deleted RLC components.
1757
+
1758
+
1759
+ Examples
1760
+ --------
1761
+
1762
+ >>> from pyedb import Edb
1763
+ >>> edbapp = Edb("myaedbfolder")
1764
+ >>> list_of_deleted_rlcs = edbapp.components.delete_single_pin_rlc()
1765
+ >>> print(list_of_deleted_rlcs)
1766
+
1767
+ """
1768
+ deleted_comps = []
1769
+ for comp, val in self.instances.items():
1770
+ if val.numpins < 2 and val.type in ["Resistor", "Capacitor", "Inductor"]:
1771
+ if deactivate_only:
1772
+ val.is_enabled = False
1773
+ val.model_type = "RLC"
1774
+ else:
1775
+ val.edbcomponent.Delete()
1776
+ deleted_comps.append(comp)
1777
+ if not deactivate_only:
1778
+ self.refresh_components()
1779
+ self._pedb._logger.info("Deleted {} components".format(len(deleted_comps)))
1780
+
1781
+ return deleted_comps
1782
+
1783
+ @pyedb_function_handler()
1784
+ def delete_component(self, component_name): # pragma: no cover
1785
+ """Delete a component.
1786
+
1787
+ .. deprecated:: 0.6.62
1788
+ Use :func:`delete` method instead.
1789
+
1790
+ Parameters
1791
+ ----------
1792
+ component_name : str
1793
+ Name of the component.
1794
+
1795
+ Returns
1796
+ -------
1797
+ bool
1798
+ ``True`` when successful, ``False`` when failed.
1799
+
1800
+ Examples
1801
+ --------
1802
+
1803
+ >>> from pyedb import Edb
1804
+ >>> edbapp = Edb("myaedbfolder")
1805
+ >>> edbapp.components.delete("A1")
1806
+
1807
+ """
1808
+ warnings.warn("`delete_component` is deprecated. Use `delete` property instead.", DeprecationWarning)
1809
+ return self.delete(component_name=component_name)
1810
+
1811
+ @pyedb_function_handler()
1812
+ def delete(self, component_name):
1813
+ """Delete a component.
1814
+
1815
+ Parameters
1816
+ ----------
1817
+ component_name : str
1818
+ Name of the component.
1819
+
1820
+ Returns
1821
+ -------
1822
+ bool
1823
+ ``True`` when successful, ``False`` when failed.
1824
+
1825
+ Examples
1826
+ --------
1827
+
1828
+ >>> from pyedb import Edb
1829
+ >>> edbapp = Edb("myaedbfolder")
1830
+ >>> edbapp.components.delete("A1")
1831
+
1832
+ """
1833
+ edb_cmp = self.get_component_by_name(component_name)
1834
+ if edb_cmp is not None:
1835
+ edb_cmp.Delete()
1836
+ if edb_cmp in list(self.instances.keys()):
1837
+ del self.components[edb_cmp]
1838
+ return True
1839
+ return False
1840
+
1841
+ @pyedb_function_handler()
1842
+ def disable_rlc_component(self, component_name):
1843
+ """Disable a RLC component.
1844
+
1845
+ Parameters
1846
+ ----------
1847
+ component_name : str
1848
+ Name of the RLC component.
1849
+
1850
+ Returns
1851
+ -------
1852
+ bool
1853
+ ``True`` when successful, ``False`` when failed.
1854
+
1855
+ Examples
1856
+ --------
1857
+
1858
+ >>> from pyedb import Edb
1859
+ >>> edbapp = Edb("myaedbfolder")
1860
+ >>> edbapp.components.disable_rlc_component("A1")
1861
+
1862
+ """
1863
+ edb_cmp = self.get_component_by_name(component_name)
1864
+ if edb_cmp is not None:
1865
+ rlc_property = edb_cmp.GetComponentProperty().Clone()
1866
+ pin_pair_model = rlc_property.GetModel().Clone()
1867
+ pprlc = pin_pair_model.GetPinPairRlc(list(pin_pair_model.PinPairs)[0])
1868
+ pprlc.CEnabled = False
1869
+ pprlc.LEnabled = False
1870
+ pprlc.REnabled = False
1871
+ pin_pair_model.SetPinPairRlc(list(pin_pair_model.PinPairs)[0], pprlc)
1872
+ rlc_property.SetModel(pin_pair_model)
1873
+ edb_cmp.SetComponentProperty(rlc_property)
1874
+ return True
1875
+ return False
1876
+
1877
+ @pyedb_function_handler()
1878
+ def set_solder_ball(
1879
+ self,
1880
+ component="",
1881
+ sball_diam=None,
1882
+ sball_height=None,
1883
+ shape="Cylinder",
1884
+ sball_mid_diam=None,
1885
+ chip_orientation="chip_down",
1886
+ auto_reference_size=True,
1887
+ reference_size_x=0,
1888
+ reference_size_y=0,
1889
+ reference_height=0,
1890
+ ):
1891
+ """Set cylindrical solder balls on a given component.
1892
+
1893
+ Parameters
1894
+ ----------
1895
+ component : str or EDB component, optional
1896
+ Name of the discrete component.
1897
+ sball_diam : str, float, optional
1898
+ Diameter of the solder ball.
1899
+ sball_height : str, float, optional
1900
+ Height of the solder ball.
1901
+ shape : str, optional
1902
+ Shape of solder ball. Options are ``"Cylinder"``,
1903
+ ``"Spheroid"``. The default is ``"Cylinder"``.
1904
+ sball_mid_diam : str, float, optional
1905
+ Mid diameter of the solder ball.
1906
+ chip_orientation : str, optional
1907
+ Give the chip orientation, ``"chip_down"`` or ``"chip_up"``. Default is ``"chip_down"``. Only applicable on
1908
+ IC model.
1909
+ auto_reference_size : bool, optional
1910
+ Whether to automatically set reference size.
1911
+ reference_size_x : int, str, float, optional
1912
+ X size of the reference. Applicable when auto_reference_size is False.
1913
+ reference_size_y : int, str, float, optional
1914
+ Y size of the reference. Applicable when auto_reference_size is False.
1915
+ reference_height : int, str, float, optional
1916
+ Height of the reference. Applicable when auto_reference_size is False.
1917
+
1918
+ Returns
1919
+ -------
1920
+ bool
1921
+ ``True`` when successful, ``False`` when failed.
1922
+
1923
+ Examples
1924
+ --------
1925
+
1926
+ >>> from pyedb import Edb
1927
+ >>> edbapp = Edb("myaedbfolder")
1928
+ >>> edbapp.components.set_solder_ball("A1")
1929
+
1930
+ """
1931
+ if not isinstance(component, self._pedb.edb_api.cell.hierarchy.component):
1932
+ edb_cmp = self.get_component_by_name(component)
1933
+ cmp = self.instances[component]
1934
+ else: # pragma: no cover
1935
+ edb_cmp = component
1936
+ cmp = self.instances[edb_cmp.GetName()]
1937
+
1938
+ cmp_type = edb_cmp.GetComponentType()
1939
+ if not sball_diam:
1940
+ pin1 = list(cmp.pins.values())[0].pin
1941
+ pin_layers = pin1.GetPadstackDef().GetData().GetLayerNames()
1942
+ pad_params = self._padstack.get_pad_parameters(pin=pin1, layername=pin_layers[0], pad_type=0)
1943
+ _sb_diam = min([self._get_edb_value(val).ToDouble() for val in pad_params[1]])
1944
+ sball_diam = _sb_diam
1945
+ if sball_height:
1946
+ sball_height = round(self._edb.utility.Value(sball_height).ToDouble(), 9)
1947
+ else:
1948
+ sball_height = round(self._edb.utility.Value(sball_diam).ToDouble(), 9) / 2
1949
+
1950
+ if not sball_mid_diam:
1951
+ sball_mid_diam = sball_diam
1952
+
1953
+ if shape == "Cylinder":
1954
+ sball_shape = self._edb.definition.SolderballShape.Cylinder
1955
+ else:
1956
+ sball_shape = self._edb.definition.SolderballShape.Spheroid
1957
+
1958
+ cmp_property = edb_cmp.GetComponentProperty().Clone()
1959
+ if cmp_type == self._edb.definition.ComponentType.IC:
1960
+ ic_die_prop = cmp_property.GetDieProperty().Clone()
1961
+ ic_die_prop.SetType(self._edb.definition.DieType.FlipChip)
1962
+ if chip_orientation.lower() == "chip_up":
1963
+ ic_die_prop.SetOrientation(self._edb.definition.DieOrientation.ChipUp)
1964
+ else:
1965
+ ic_die_prop.SetOrientation(self._edb.definition.DieOrientation.ChipDown)
1966
+ cmp_property.SetDieProperty(ic_die_prop)
1967
+
1968
+ solder_ball_prop = cmp_property.GetSolderBallProperty().Clone()
1969
+ solder_ball_prop.SetDiameter(self._get_edb_value(sball_diam), self._get_edb_value(sball_mid_diam))
1970
+ solder_ball_prop.SetHeight(self._get_edb_value(sball_height))
1971
+
1972
+ solder_ball_prop.SetShape(sball_shape)
1973
+ cmp_property.SetSolderBallProperty(solder_ball_prop)
1974
+
1975
+ port_prop = cmp_property.GetPortProperty().Clone()
1976
+ port_prop.SetReferenceHeight(self._pedb.edb_value(reference_height))
1977
+ port_prop.SetReferenceSizeAuto(auto_reference_size)
1978
+ if not auto_reference_size:
1979
+ port_prop.SetReferenceSize(self._pedb.edb_value(reference_size_x), self._pedb.edb_value(reference_size_y))
1980
+ cmp_property.SetPortProperty(port_prop)
1981
+ edb_cmp.SetComponentProperty(cmp_property)
1982
+ return True
1983
+
1984
+ @pyedb_function_handler()
1985
+ def set_component_rlc(
1986
+ self,
1987
+ componentname,
1988
+ res_value=None,
1989
+ ind_value=None,
1990
+ cap_value=None,
1991
+ isparallel=False,
1992
+ ):
1993
+ """Update values for an RLC component.
1994
+
1995
+ Parameters
1996
+ ----------
1997
+ componentname :
1998
+ Name of the RLC component.
1999
+ res_value : float, optional
2000
+ Resistance value. The default is ``None``.
2001
+ ind_value : float, optional
2002
+ Inductor value. The default is ``None``.
2003
+ cap_value : float optional
2004
+ Capacitor value. The default is ``None``.
2005
+ isparallel : bool, optional
2006
+ Whether the RLC component is parallel. The default is ``False``.
2007
+
2008
+ Returns
2009
+ -------
2010
+ bool
2011
+ ``True`` when successful, ``False`` when failed.
2012
+
2013
+ Examples
2014
+ --------
2015
+
2016
+ >>> from pyedb import Edb
2017
+ >>> edbapp = Edb("myaedbfolder")
2018
+ >>> edbapp.components.set_component_rlc(
2019
+ ... "R1", res_value=50, ind_value=1e-9, cap_value=1e-12, isparallel=False
2020
+ ... )
2021
+
2022
+ """
2023
+ if res_value is None and ind_value is None and cap_value is None:
2024
+ self.instances[componentname].is_enabled = False
2025
+ self._logger.info("No parameters passed, component %s is disabled.", componentname)
2026
+ return True
2027
+ edb_component = self.get_component_by_name(componentname)
2028
+ edb_rlc_component_property = self._edb.cell.hierarchy._hierarchy.RLCComponentProperty()
2029
+ component_pins = self.get_pin_from_component(componentname)
2030
+ pin_number = len(component_pins)
2031
+ if pin_number == 2:
2032
+ from_pin = component_pins[0]
2033
+ to_pin = component_pins[1]
2034
+ rlc = self._edb.utility.utility.Rlc()
2035
+ rlc.IsParallel = isparallel
2036
+ if res_value is not None:
2037
+ rlc.REnabled = True
2038
+ rlc.R = self._get_edb_value(res_value)
2039
+ else:
2040
+ rlc.REnabled = False
2041
+ if ind_value is not None:
2042
+ rlc.LEnabled = True
2043
+ rlc.L = self._get_edb_value(ind_value)
2044
+ else:
2045
+ rlc.LEnabled = False
2046
+ if cap_value is not None:
2047
+ rlc.CEnabled = True
2048
+ rlc.C = self._get_edb_value(cap_value)
2049
+ else:
2050
+ rlc.CEnabled = False
2051
+ pin_pair = self._edb.utility.utility.PinPair(from_pin.GetName(), to_pin.GetName())
2052
+ rlc_model = self._edb.cell.hierarchy._hierarchy.PinPairModel()
2053
+ rlc_model.SetPinPairRlc(pin_pair, rlc)
2054
+ if not edb_rlc_component_property.SetModel(rlc_model) or not edb_component.SetComponentProperty(
2055
+ edb_rlc_component_property
2056
+ ):
2057
+ self._logger.error("Failed to set RLC model on component")
2058
+ return False
2059
+ else:
2060
+ self._logger.warning(
2061
+ "Component %s has not been assigned because either it is not present in the layout "
2062
+ "or it contains a number of pins not equal to 2",
2063
+ componentname,
2064
+ )
2065
+ return False
2066
+ self._logger.info("RLC properties for Component %s has been assigned.", componentname)
2067
+ return True
2068
+
2069
+ @pyedb_function_handler()
2070
+ def update_rlc_from_bom(
2071
+ self,
2072
+ bom_file,
2073
+ delimiter=";",
2074
+ valuefield="Func des",
2075
+ comptype="Prod name",
2076
+ refdes="Pos / Place",
2077
+ ):
2078
+ """Update the EDC core component values (RLCs) with values coming from a BOM file.
2079
+
2080
+ Parameters
2081
+ ----------
2082
+ bom_file : str
2083
+ Full path to the BOM file, which is a delimited text file.
2084
+ Header values needed inside the BOM reader must
2085
+ be explicitly set if different from the defaults.
2086
+ delimiter : str, optional
2087
+ Value to use for the delimiter. The default is ``";"``.
2088
+ valuefield : str, optional
2089
+ Field header containing the value of the component. The default is ``"Func des"``.
2090
+ The value for this parameter must being with the value of the component
2091
+ followed by a space and then the rest of the value. For example, ``"22pF"``.
2092
+ comptype : str, optional
2093
+ Field header containing the type of component. The default is ``"Prod name"``. For
2094
+ example, you might enter ``"Inductor"``.
2095
+ refdes : str, optional
2096
+ Field header containing the reference designator of the component. The default is
2097
+ ``"Pos / Place"``. For example, you might enter ``"C100"``.
2098
+
2099
+ Returns
2100
+ -------
2101
+ bool
2102
+ ``True`` if the file contains the header and it is correctly parsed. ``True`` is
2103
+ returned even if no values are assigned.
2104
+
2105
+ """
2106
+ with open(bom_file, "r") as f:
2107
+ Lines = f.readlines()
2108
+ found = False
2109
+ refdescolumn = None
2110
+ comptypecolumn = None
2111
+ valuecolumn = None
2112
+ unmount_comp_list = list(self.instances.keys())
2113
+ for line in Lines:
2114
+ content_line = [i.strip() for i in line.split(delimiter)]
2115
+ if valuefield in content_line:
2116
+ valuecolumn = content_line.index(valuefield)
2117
+ if comptype in content_line:
2118
+ comptypecolumn = content_line.index(comptype)
2119
+ if refdes in content_line:
2120
+ refdescolumn = content_line.index(refdes)
2121
+ elif refdescolumn:
2122
+ found = True
2123
+ new_refdes = content_line[refdescolumn].split(" ")[0]
2124
+ new_value = content_line[valuecolumn].split(" ")[0]
2125
+ new_type = content_line[comptypecolumn]
2126
+ if "resistor" in new_type.lower():
2127
+ self.set_component_rlc(new_refdes, res_value=new_value)
2128
+ unmount_comp_list.remove(new_refdes)
2129
+ elif "capacitor" in new_type.lower():
2130
+ self.set_component_rlc(new_refdes, cap_value=new_value)
2131
+ unmount_comp_list.remove(new_refdes)
2132
+ elif "inductor" in new_type.lower():
2133
+ self.set_component_rlc(new_refdes, ind_value=new_value)
2134
+ unmount_comp_list.remove(new_refdes)
2135
+ for comp in unmount_comp_list:
2136
+ self.components[comp].is_enabled = False
2137
+ return found
2138
+
2139
+ @pyedb_function_handler()
2140
+ def import_bom(
2141
+ self,
2142
+ bom_file,
2143
+ delimiter=",",
2144
+ refdes_col=0,
2145
+ part_name_col=1,
2146
+ comp_type_col=2,
2147
+ value_col=3,
2148
+ ):
2149
+ """Load external BOM file.
2150
+
2151
+ Parameters
2152
+ ----------
2153
+ bom_file : str
2154
+ Full path to the BOM file, which is a delimited text file.
2155
+ delimiter : str, optional
2156
+ Value to use for the delimiter. The default is ``","``.
2157
+ refdes_col : int, optional
2158
+ Column index of reference designator. The default is ``"0"``.
2159
+ part_name_col : int, optional
2160
+ Column index of part name. The default is ``"1"``. Set to ``None`` if
2161
+ the column does not exist.
2162
+ comp_type_col : int, optional
2163
+ Column index of component type. The default is ``"2"``.
2164
+ value_col : int, optional
2165
+ Column index of value. The default is ``"3"``. Set to ``None``
2166
+ if the column does not exist.
2167
+
2168
+ Returns
2169
+ -------
2170
+ bool
2171
+ """
2172
+ with open(bom_file, "r") as f:
2173
+ lines = f.readlines()
2174
+ unmount_comp_list = list(self.instances.keys())
2175
+ for l in lines[1:]:
2176
+ l = l.replace(" ", "").replace("\n", "")
2177
+ if not l:
2178
+ continue
2179
+ l = l.split(delimiter)
2180
+
2181
+ refdes = l[refdes_col]
2182
+ comp = self.components[refdes]
2183
+ if not part_name_col == None:
2184
+ part_name = l[part_name_col]
2185
+ if comp.partname == part_name:
2186
+ pass
2187
+ else:
2188
+ pinlist = self.get_pin_from_component(refdes)
2189
+ if not part_name in self.definitions:
2190
+ footprint_cell = self.definitions[comp.partname]._edb_object.GetFootprintCell()
2191
+ comp_def = self._edb.definition.ComponentDef.Create(self._db, part_name, footprint_cell)
2192
+ for pin in pinlist:
2193
+ self._edb.definition.ComponentDefPin.Create(comp_def, pin.GetName())
2194
+
2195
+ p_layer = comp.placement_layer
2196
+ refdes_temp = comp.refdes + "_temp"
2197
+ comp.refdes = refdes_temp
2198
+
2199
+ unmount_comp_list.remove(refdes)
2200
+ comp.edbcomponent.Ungroup(True)
2201
+
2202
+ self.create(pinlist, refdes, p_layer, part_name)
2203
+ self.refresh_components()
2204
+ comp = self.components[refdes]
2205
+
2206
+ comp_type = l[comp_type_col]
2207
+ if comp_type.capitalize() in ["Resistor", "Capacitor", "Inductor", "Other"]:
2208
+ comp.type = comp_type.capitalize()
2209
+ else:
2210
+ comp.type = comp_type.upper()
2211
+
2212
+ if comp_type.capitalize() in ["Resistor", "Capacitor", "Inductor"] and refdes in unmount_comp_list:
2213
+ unmount_comp_list.remove(refdes)
2214
+ if not value_col == None:
2215
+ try:
2216
+ value = l[value_col]
2217
+ except:
2218
+ value = None
2219
+ if value:
2220
+ if comp_type == "Resistor":
2221
+ self.set_component_rlc(refdes, res_value=value)
2222
+ elif comp_type == "Capacitor":
2223
+ self.set_component_rlc(refdes, cap_value=value)
2224
+ elif comp_type == "Inductor":
2225
+ self.set_component_rlc(refdes, ind_value=value)
2226
+ for comp in unmount_comp_list:
2227
+ self.components[comp].is_enabled = False
2228
+ return True
2229
+
2230
+ @pyedb_function_handler()
2231
+ def export_bom(self, bom_file, delimiter=","):
2232
+ """Export Bom file from layout.
2233
+
2234
+ Parameters
2235
+ ----------
2236
+ bom_file : str
2237
+ Full path to the BOM file, which is a delimited text file.
2238
+ delimiter : str, optional
2239
+ Value to use for the delimiter. The default is ``","``.
2240
+ """
2241
+ with open(bom_file, "w") as f:
2242
+ f.writelines([delimiter.join(["RefDes", "Part name", "Type", "Value\n"])])
2243
+ for refdes, comp in self.instances.items():
2244
+ if not comp.is_enabled and comp.type in ["Resistor", "Capacitor", "Inductor"]:
2245
+ continue
2246
+ part_name = comp.partname
2247
+ comp_type = comp.type
2248
+ if comp_type == "Resistor":
2249
+ value = comp.res_value
2250
+ elif comp_type == "Capacitor":
2251
+ value = comp.cap_value
2252
+ elif comp_type == "Inductor":
2253
+ value = comp.ind_value
2254
+ else:
2255
+ value = ""
2256
+ if not value:
2257
+ value = ""
2258
+ f.writelines([delimiter.join([refdes, part_name, comp_type, value + "\n"])])
2259
+ return True
2260
+
2261
+ @pyedb_function_handler()
2262
+ def get_pin_from_component(self, component, netName=None, pinName=None):
2263
+ """Retrieve the pins of a component.
2264
+
2265
+ Parameters
2266
+ ----------
2267
+ component : str or EDB component
2268
+ Name of the component or the EDB component object.
2269
+ netName : str, optional
2270
+ Filter on the net name as an alternative to
2271
+ ``pinName``. The default is ``None``.
2272
+ pinName : str, optional
2273
+ Filter on the pin name an an alternative to
2274
+ ``netName``. The default is ``None``.
2275
+
2276
+ Returns
2277
+ -------
2278
+ list
2279
+ List of pins when the component is found or ``[]`` otherwise.
2280
+
2281
+ Examples
2282
+ --------
2283
+
2284
+ >>> from pyedb import Edb
2285
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2286
+ >>> edbapp.components.get_pin_from_component("R1", refdes)
2287
+
2288
+ """
2289
+ if not isinstance(component, self._pedb.edb_api.cell.hierarchy.component):
2290
+ component = self._pedb.edb_api.cell.hierarchy.component.FindByName(self._active_layout, component)
2291
+ if netName:
2292
+ if not isinstance(netName, list):
2293
+ netName = [netName]
2294
+ pins = [
2295
+ p
2296
+ for p in list(component.LayoutObjs)
2297
+ if int(p.GetObjType()) == 1 and p.IsLayoutPin() and p.GetNet().GetName() in netName
2298
+ ]
2299
+ elif pinName:
2300
+ if not isinstance(pinName, list):
2301
+ pinName = [pinName]
2302
+ pins = [
2303
+ p
2304
+ for p in list(component.LayoutObjs)
2305
+ if int(p.GetObjType()) == 1
2306
+ and p.IsLayoutPin()
2307
+ and (self.get_aedt_pin_name(p) in pinName or p.GetName() in pinName)
2308
+ ]
2309
+ else:
2310
+ pins = [p for p in list(component.LayoutObjs) if int(p.GetObjType()) == 1 and p.IsLayoutPin()]
2311
+ return pins
2312
+
2313
+ @pyedb_function_handler()
2314
+ def get_aedt_pin_name(self, pin):
2315
+ """Retrieve the pin name that is shown in AEDT.
2316
+
2317
+ .. note::
2318
+ To obtain the EDB core pin name, use `pin.GetName()`.
2319
+
2320
+ Parameters
2321
+ ----------
2322
+ pin : str
2323
+ Name of the pin in EDB core.
2324
+
2325
+ Returns
2326
+ -------
2327
+ str
2328
+ Name of the pin in AEDT.
2329
+
2330
+ Examples
2331
+ --------
2332
+
2333
+ >>> from pyedb import Edb
2334
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2335
+ >>> edbapp.components.get_aedt_pin_name(pin)
2336
+
2337
+ """
2338
+ if isinstance(pin, EDBPadstackInstance):
2339
+ pin = pin._edb_padstackinstance
2340
+ val = String("")
2341
+ _, name = pin.GetProductProperty(self._edb.edb_api.ProductId.Designer, 11, val)
2342
+ name = str(name).strip("'")
2343
+ return name
2344
+
2345
+ @pyedb_function_handler()
2346
+ def get_pin_position(self, pin):
2347
+ """Retrieve the pin position in meters.
2348
+
2349
+ Parameters
2350
+ ----------
2351
+ pin : str
2352
+ Name of the pin.
2353
+
2354
+ Returns
2355
+ -------
2356
+ list
2357
+ Pin position as a list of float values in the form ``[x, y]``.
2358
+
2359
+ Examples
2360
+ --------
2361
+
2362
+ >>> from pyedb import Edb
2363
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2364
+ >>> edbapp.components.get_pin_position(pin)
2365
+
2366
+ """
2367
+ res, pt_pos, rot_pos = pin.GetPositionAndRotation()
2368
+
2369
+ if pin.GetComponent().IsNull():
2370
+ transformed_pt_pos = pt_pos
2371
+ else:
2372
+ transformed_pt_pos = pin.GetComponent().GetTransform().TransformPoint(pt_pos)
2373
+ pin_xy = self._edb.geometry.point_data(
2374
+ self._get_edb_value(str(transformed_pt_pos.X.ToDouble())),
2375
+ self._get_edb_value(str(transformed_pt_pos.Y.ToDouble())),
2376
+ )
2377
+ return [pin_xy.X.ToDouble(), pin_xy.Y.ToDouble()]
2378
+
2379
+ @pyedb_function_handler()
2380
+ def get_pins_name_from_net(self, pin_list, net_name):
2381
+ """Retrieve pins belonging to a net.
2382
+
2383
+ Parameters
2384
+ ----------
2385
+ pin_list : list
2386
+ List of pins to check.
2387
+ net_name : str
2388
+ Name of the net.
2389
+
2390
+ Returns
2391
+ -------
2392
+ list
2393
+ List of pins belong to the net.
2394
+
2395
+ Examples
2396
+ --------
2397
+
2398
+ >>> from pyedb import Edb
2399
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2400
+ >>> edbapp.components.get_pins_name_from_net(pin_list, net_name)
2401
+
2402
+ """
2403
+ pinlist = []
2404
+ for pin in pin_list:
2405
+ if pin.GetNet().GetName() == net_name:
2406
+ pinlist.append(pin.GetName())
2407
+ return pinlist
2408
+
2409
+ @pyedb_function_handler()
2410
+ def get_nets_from_pin_list(self, PinList):
2411
+ """Retrieve nets with one or more pins.
2412
+
2413
+ Parameters
2414
+ ----------
2415
+ PinList : list
2416
+ List of pins.
2417
+
2418
+ Returns
2419
+ -------
2420
+ list
2421
+ List of nets with one or more pins.
2422
+
2423
+ Examples
2424
+ --------
2425
+
2426
+ >>> from pyedb import Edb
2427
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2428
+ >>> edbapp.components.get_nets_from_pin_list(pinlist)
2429
+
2430
+ """
2431
+ netlist = []
2432
+ for pin in PinList:
2433
+ netlist.append(pin.GetNet().GetName())
2434
+ return list(set(netlist))
2435
+
2436
+ @pyedb_function_handler()
2437
+ def get_component_net_connection_info(self, refdes):
2438
+ """Retrieve net connection information.
2439
+
2440
+ Parameters
2441
+ ----------
2442
+ refdes :
2443
+ Reference designator for the net.
2444
+
2445
+ Returns
2446
+ -------
2447
+ dict
2448
+ Dictionary of the net connection information for the reference designator.
2449
+
2450
+ Examples
2451
+ --------
2452
+
2453
+ >>> from pyedb import Edb
2454
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2455
+ >>> edbapp.components.get_component_net_connection_info(refdes)
2456
+
2457
+ """
2458
+ component_pins = self.get_pin_from_component(refdes)
2459
+ data = {"refdes": [], "pin_name": [], "net_name": []}
2460
+ for pin_obj in component_pins:
2461
+ pin_name = pin_obj.GetName()
2462
+ net_name = pin_obj.GetNet().GetName()
2463
+ if pin_name is not None:
2464
+ data["refdes"].append(refdes)
2465
+ data["pin_name"].append(pin_name)
2466
+ data["net_name"].append(net_name)
2467
+ return data
2468
+
2469
+ def get_rats(self):
2470
+ """Retrieve a list of dictionaries of the reference designator, pin names, and net names.
2471
+
2472
+ Returns
2473
+ -------
2474
+ list
2475
+ List of dictionaries of the reference designator, pin names,
2476
+ and net names.
2477
+
2478
+ Examples
2479
+ --------
2480
+
2481
+ >>> from pyedb import Edb
2482
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2483
+ >>> edbapp.components.get_rats()
2484
+
2485
+ """
2486
+ df_list = []
2487
+ for refdes in self.instances.keys():
2488
+ df = self.get_component_net_connection_info(refdes)
2489
+ df_list.append(df)
2490
+ return df_list
2491
+
2492
+ def get_through_resistor_list(self, threshold=1):
2493
+ """Retrieve through resistors.
2494
+
2495
+ Parameters
2496
+ ----------
2497
+ threshold : int, optional
2498
+ Threshold value. The default is ``1``.
2499
+
2500
+ Returns
2501
+ -------
2502
+ list
2503
+ List of through resistors.
2504
+
2505
+ Examples
2506
+ --------
2507
+
2508
+ >>> from pyedb import Edb
2509
+ >>> edbapp = Edb("myaedbfolder", "project name", "release version")
2510
+ >>> edbapp.components.get_through_resistor_list()
2511
+
2512
+ """
2513
+ through_comp_list = []
2514
+ for refdes, comp_obj in self.resistors.items():
2515
+ numpins = comp_obj.numpins
2516
+
2517
+ if numpins == 2:
2518
+ value = comp_obj.res_value
2519
+ value = resistor_value_parser(value)
2520
+
2521
+ if value <= threshold:
2522
+ through_comp_list.append(refdes)
2523
+
2524
+ return through_comp_list
2525
+
2526
+ @pyedb_function_handler()
2527
+ def short_component_pins(self, component_name, pins_to_short=None, width=1e-3):
2528
+ """Short pins of component with a trace.
2529
+
2530
+ Parameters
2531
+ ----------
2532
+ component_name : str
2533
+ Name of the component.
2534
+ pins_to_short : list, optional
2535
+ List of pins to short. If `None`, all pins will be shorted.
2536
+ width : float, optional
2537
+ Short Trace width. It will be used in trace computation algorithm
2538
+
2539
+ Returns
2540
+ -------
2541
+ bool
2542
+ ``True`` when successful, ``False`` when failed.
2543
+
2544
+ Examples
2545
+ --------
2546
+
2547
+ >>> from pyedb import Edb
2548
+ >>> edbapp = Edb("myaedbfolder")
2549
+ >>> edbapp.components.short_component_pins("J4A2", ["G4", "9", "3"])
2550
+
2551
+ """
2552
+ component = self.components[component_name]
2553
+ pins = component.pins
2554
+ pins_list = []
2555
+
2556
+ component.center
2557
+ for pin_name, pin in pins.items():
2558
+ if pins_to_short:
2559
+ if pin_name in pins_to_short:
2560
+ pins_list.append(pin)
2561
+ else:
2562
+ pins_list.append(pin)
2563
+ positions_to_short = []
2564
+ center = component.center
2565
+ c = [center[0], center[1], 0]
2566
+ delta_pins = []
2567
+ w = width
2568
+ for pin in pins_list:
2569
+ placement_layer = pin.placement_layer
2570
+ positions_to_short.append(pin.position)
2571
+ if placement_layer in self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer:
2572
+ pad = self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer[placement_layer]
2573
+ else:
2574
+ layer = list(self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer.keys())[
2575
+ 0
2576
+ ]
2577
+ pad = self._pedb.padstacks.definitions[pin.pin.GetPadstackDef().GetName()].pad_by_layer[layer]
2578
+ pars = pad.parameters_values
2579
+ geom = pad.geometry_type
2580
+ if geom < 6 and pars:
2581
+ delta_pins.append(max(pars) + min(pars) / 2)
2582
+ w = min(min(pars), w)
2583
+ elif pars:
2584
+ delta_pins.append(1.5 * pars[0])
2585
+ w = min(pars[0], w)
2586
+ elif pad.polygon_data.edb_api: # pragma: no cover
2587
+ bbox = pad.polygon_data.edb_api.GetBBox()
2588
+ lower = [bbox.Item1.X.ToDouble(), bbox.Item1.Y.ToDouble()]
2589
+ upper = [bbox.Item2.X.ToDouble(), bbox.Item2.Y.ToDouble()]
2590
+ pars = [abs(lower[0] - upper[0]), abs(lower[1] - upper[1])]
2591
+ delta_pins.append(max(pars) + min(pars) / 2)
2592
+ w = min(min(pars), w)
2593
+ else:
2594
+ delta_pins.append(1.5 * width)
2595
+ i = 0
2596
+
2597
+ while i < len(positions_to_short) - 1:
2598
+ p0 = []
2599
+ p0.append([positions_to_short[i][0] - delta_pins[i], positions_to_short[i][1], 0])
2600
+ p0.append([positions_to_short[i][0] + delta_pins[i], positions_to_short[i][1], 0])
2601
+ p0.append([positions_to_short[i][0], positions_to_short[i][1] - delta_pins[i], 0])
2602
+ p0.append([positions_to_short[i][0], positions_to_short[i][1] + delta_pins[i], 0])
2603
+ p0.append([positions_to_short[i][0], positions_to_short[i][1], 0])
2604
+ l0 = [
2605
+ GeometryOperators.points_distance(p0[0], c),
2606
+ GeometryOperators.points_distance(p0[1], c),
2607
+ GeometryOperators.points_distance(p0[2], c),
2608
+ GeometryOperators.points_distance(p0[3], c),
2609
+ GeometryOperators.points_distance(p0[4], c),
2610
+ ]
2611
+ l0_min = l0.index(min(l0))
2612
+ p1 = []
2613
+ p1.append(
2614
+ [
2615
+ positions_to_short[i + 1][0] - delta_pins[i + 1],
2616
+ positions_to_short[i + 1][1],
2617
+ 0,
2618
+ ]
2619
+ )
2620
+ p1.append(
2621
+ [
2622
+ positions_to_short[i + 1][0] + delta_pins[i + 1],
2623
+ positions_to_short[i + 1][1],
2624
+ 0,
2625
+ ]
2626
+ )
2627
+ p1.append(
2628
+ [
2629
+ positions_to_short[i + 1][0],
2630
+ positions_to_short[i + 1][1] - delta_pins[i + 1],
2631
+ 0,
2632
+ ]
2633
+ )
2634
+ p1.append(
2635
+ [
2636
+ positions_to_short[i + 1][0],
2637
+ positions_to_short[i + 1][1] + delta_pins[i + 1],
2638
+ 0,
2639
+ ]
2640
+ )
2641
+ p1.append([positions_to_short[i + 1][0], positions_to_short[i + 1][1], 0])
2642
+
2643
+ l1 = [
2644
+ GeometryOperators.points_distance(p1[0], c),
2645
+ GeometryOperators.points_distance(p1[1], c),
2646
+ GeometryOperators.points_distance(p1[2], c),
2647
+ GeometryOperators.points_distance(p1[3], c),
2648
+ GeometryOperators.points_distance(p1[4], c),
2649
+ ]
2650
+ l1_min = l1.index(min(l1))
2651
+
2652
+ trace_points = [positions_to_short[i]]
2653
+
2654
+ trace_points.append(p0[l0_min][:2])
2655
+ trace_points.append(c[:2])
2656
+ trace_points.append(p1[l1_min][:2])
2657
+
2658
+ trace_points.append(positions_to_short[i + 1])
2659
+
2660
+ self._pedb.modeler.create_trace(
2661
+ trace_points,
2662
+ layer_name=placement_layer,
2663
+ net_name="short",
2664
+ width=w,
2665
+ start_cap_style="Flat",
2666
+ end_cap_style="Flat",
2667
+ )
2668
+ i += 1
2669
+ return True