pyedb 0.48.0__py3-none-any.whl → 0.50.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.
- pyedb/__init__.py +1 -1
- pyedb/configuration/cfg_modeler.py +42 -11
- pyedb/configuration/cfg_ports_sources.py +9 -1
- pyedb/configuration/cfg_stackup.py +4 -4
- pyedb/dotnet/database/cell/hierarchy/component.py +64 -8
- pyedb/dotnet/database/cell/hierarchy/s_parameter_model.py +7 -0
- pyedb/dotnet/database/cell/terminal/terminal.py +3 -3
- pyedb/dotnet/database/components.py +68 -46
- pyedb/dotnet/database/definition/package_def.py +29 -5
- pyedb/dotnet/database/edb_data/padstacks_data.py +5 -5
- pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
- pyedb/dotnet/database/edb_data/variables.py +3 -3
- pyedb/dotnet/database/geometry/polygon_data.py +14 -1
- pyedb/dotnet/database/materials.py +16 -16
- pyedb/dotnet/database/modeler.py +50 -9
- pyedb/dotnet/database/padstack.py +7 -5
- pyedb/dotnet/database/sim_setup_data/data/settings.py +28 -0
- pyedb/dotnet/database/siwave.py +36 -32
- pyedb/dotnet/database/stackup.py +1 -0
- pyedb/dotnet/database/utilities/hfss_simulation_setup.py +5 -6
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +3 -1
- pyedb/dotnet/edb.py +22 -20
- pyedb/extensions/__init__.py +0 -0
- pyedb/extensions/via_design_backend.py +681 -0
- pyedb/grpc/database/components.py +57 -48
- pyedb/grpc/database/definition/materials.py +33 -33
- pyedb/grpc/database/definitions.py +6 -4
- pyedb/grpc/database/hfss.py +2 -2
- pyedb/grpc/database/hierarchy/component.py +3 -2
- pyedb/grpc/database/layers/stackup_layer.py +119 -22
- pyedb/grpc/database/layout_validation.py +5 -5
- pyedb/grpc/database/primitive/path.py +1 -1
- pyedb/grpc/database/source_excitations.py +156 -0
- pyedb/grpc/database/stackup.py +50 -24
- pyedb/grpc/edb.py +115 -105
- {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/METADATA +1 -1
- {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/RECORD +39 -38
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +0 -1151
- {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/LICENSE +0 -0
- {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/WHEEL +0 -0
|
@@ -204,6 +204,18 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
204
204
|
"""
|
|
205
205
|
self.negative = value
|
|
206
206
|
|
|
207
|
+
@property
|
|
208
|
+
def is_stackup_layer(self):
|
|
209
|
+
"""Testing if layer is stackup layer.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
`True` if layer type is "signal" or "dielectric".
|
|
214
|
+
"""
|
|
215
|
+
if self.type in ["signal", "dielectric", "via", "wirebond"]:
|
|
216
|
+
return True
|
|
217
|
+
return False
|
|
218
|
+
|
|
207
219
|
@property
|
|
208
220
|
def material(self):
|
|
209
221
|
"""Material.
|
|
@@ -229,7 +241,8 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
229
241
|
Material conductivity value.
|
|
230
242
|
"""
|
|
231
243
|
if self.material in self._pedb.materials.materials:
|
|
232
|
-
|
|
244
|
+
condcutivity = self._pedb.materials[self.material].conductivity
|
|
245
|
+
return condcutivity if condcutivity else 0.0
|
|
233
246
|
return None
|
|
234
247
|
|
|
235
248
|
@property
|
|
@@ -242,7 +255,8 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
242
255
|
Material permittivity value.
|
|
243
256
|
"""
|
|
244
257
|
if self.material in self._pedb.materials.materials:
|
|
245
|
-
|
|
258
|
+
permittivity = self._pedb.materials[self.material].permittivity
|
|
259
|
+
return permittivity if permittivity else 0.0
|
|
246
260
|
return None
|
|
247
261
|
|
|
248
262
|
@property
|
|
@@ -255,7 +269,8 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
255
269
|
Material loss tangent value.
|
|
256
270
|
"""
|
|
257
271
|
if self.material in self._pedb.materials.materials:
|
|
258
|
-
|
|
272
|
+
loss_tangent = self._pedb.materials[self.material].loss_tangent
|
|
273
|
+
return loss_tangent if loss_tangent else 0.0
|
|
259
274
|
return None
|
|
260
275
|
|
|
261
276
|
@property
|
|
@@ -270,7 +285,7 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
270
285
|
if self.type == "signal":
|
|
271
286
|
return self.get_fill_material()
|
|
272
287
|
else:
|
|
273
|
-
return
|
|
288
|
+
return None
|
|
274
289
|
|
|
275
290
|
@dielectric_fill.setter
|
|
276
291
|
def dielectric_fill(self, name):
|
|
@@ -327,9 +342,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
327
342
|
if len(top_roughness_model) == 2:
|
|
328
343
|
return top_roughness_model[0].value
|
|
329
344
|
else:
|
|
330
|
-
return
|
|
345
|
+
return 0.0
|
|
331
346
|
except:
|
|
332
|
-
return
|
|
347
|
+
return 0.0
|
|
333
348
|
|
|
334
349
|
@top_hallhuray_nodule_radius.setter
|
|
335
350
|
def top_hallhuray_nodule_radius(self, value):
|
|
@@ -355,9 +370,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
355
370
|
if len(top_roughness_model) == 2:
|
|
356
371
|
return top_roughness_model[1].value
|
|
357
372
|
else:
|
|
358
|
-
return
|
|
373
|
+
return 0.0
|
|
359
374
|
except:
|
|
360
|
-
return
|
|
375
|
+
return 0.0
|
|
361
376
|
|
|
362
377
|
@top_hallhuray_surface_ratio.setter
|
|
363
378
|
def top_hallhuray_surface_ratio(self, value):
|
|
@@ -383,9 +398,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
383
398
|
if len(bottom_roughness_model) == 2:
|
|
384
399
|
return round(bottom_roughness_model[0].value, 9)
|
|
385
400
|
else:
|
|
386
|
-
return
|
|
401
|
+
return 0.0
|
|
387
402
|
except:
|
|
388
|
-
return
|
|
403
|
+
return 0.0
|
|
389
404
|
|
|
390
405
|
@bottom_hallhuray_nodule_radius.setter
|
|
391
406
|
def bottom_hallhuray_nodule_radius(self, value):
|
|
@@ -411,9 +426,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
411
426
|
if len(bottom_roughness_model) == 2:
|
|
412
427
|
return bottom_roughness_model[1].value
|
|
413
428
|
else:
|
|
414
|
-
return
|
|
429
|
+
return 0.0
|
|
415
430
|
except:
|
|
416
|
-
return
|
|
431
|
+
return 0.0
|
|
417
432
|
|
|
418
433
|
@bottom_hallhuray_surface_ratio.setter
|
|
419
434
|
def bottom_hallhuray_surface_ratio(self, value):
|
|
@@ -439,9 +454,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
439
454
|
side_roughness_model = self.get_roughness_model(GrpcRoughnessRegion.SIDE)
|
|
440
455
|
if len(side_roughness_model) == 2:
|
|
441
456
|
return round(side_roughness_model[0].value, 9)
|
|
442
|
-
return
|
|
457
|
+
return 0.0
|
|
443
458
|
except:
|
|
444
|
-
return
|
|
459
|
+
return 0.0
|
|
445
460
|
|
|
446
461
|
@side_hallhuray_nodule_radius.setter
|
|
447
462
|
def side_hallhuray_nodule_radius(self, value):
|
|
@@ -466,9 +481,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
466
481
|
side_roughness_model = self.get_roughness_model(GrpcRoughnessRegion.SIDE)
|
|
467
482
|
if len(side_roughness_model) == 2:
|
|
468
483
|
return side_roughness_model[1].value
|
|
469
|
-
return
|
|
484
|
+
return 0.0
|
|
470
485
|
except:
|
|
471
|
-
return
|
|
486
|
+
return 0.0
|
|
472
487
|
|
|
473
488
|
@side_hallhuray_surface_ratio.setter
|
|
474
489
|
def side_hallhuray_surface_ratio(self, value):
|
|
@@ -494,9 +509,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
494
509
|
if isinstance(top_roughness_model, GrpcValue):
|
|
495
510
|
return top_roughness_model.value
|
|
496
511
|
else:
|
|
497
|
-
return
|
|
512
|
+
return 0.0
|
|
498
513
|
except:
|
|
499
|
-
return
|
|
514
|
+
return 0.0
|
|
500
515
|
|
|
501
516
|
@top_groisse_roughness.setter
|
|
502
517
|
def top_groisse_roughness(self, value):
|
|
@@ -522,9 +537,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
522
537
|
if isinstance(bottom_roughness_model, GrpcValue):
|
|
523
538
|
return bottom_roughness_model.value
|
|
524
539
|
else:
|
|
525
|
-
return
|
|
540
|
+
return 0.0
|
|
526
541
|
except:
|
|
527
|
-
return
|
|
542
|
+
return 0.0
|
|
528
543
|
|
|
529
544
|
@bottom_groisse_roughness.setter
|
|
530
545
|
def bottom_groisse_roughness(self, value):
|
|
@@ -550,9 +565,9 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
550
565
|
if isinstance(side_roughness_model, GrpcValue):
|
|
551
566
|
return side_roughness_model.value
|
|
552
567
|
else:
|
|
553
|
-
return
|
|
568
|
+
return 0.0
|
|
554
569
|
except:
|
|
555
|
-
return
|
|
570
|
+
return 0.0
|
|
556
571
|
|
|
557
572
|
@side_groisse_roughness.setter
|
|
558
573
|
def side_groisse_roughness(self, value):
|
|
@@ -605,6 +620,13 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
605
620
|
else:
|
|
606
621
|
model = GrpcValue(groisse_roughness)
|
|
607
622
|
self.set_roughness_model(model, r)
|
|
623
|
+
if [
|
|
624
|
+
self.get_roughness_model(GrpcRoughnessRegion.TOP),
|
|
625
|
+
self.get_roughness_model(GrpcRoughnessRegion.BOTTOM),
|
|
626
|
+
self.get_roughness_model(GrpcRoughnessRegion.SIDE),
|
|
627
|
+
]:
|
|
628
|
+
return True
|
|
629
|
+
return False
|
|
608
630
|
|
|
609
631
|
@property
|
|
610
632
|
def properties(self):
|
|
@@ -649,3 +671,78 @@ class StackupLayer(GrpcStackupLayer):
|
|
|
649
671
|
data["roughness"] = roughness
|
|
650
672
|
data["etching"] = {"enabled": self.etch_factor_enabled, "factor": self.etch_factor}
|
|
651
673
|
return data
|
|
674
|
+
|
|
675
|
+
def _json_format(self):
|
|
676
|
+
dict_out = {
|
|
677
|
+
"color": self.color,
|
|
678
|
+
"dielectric_fill": self.dielectric_fill,
|
|
679
|
+
"etch_factor": self.etch_factor,
|
|
680
|
+
"material": self.material,
|
|
681
|
+
"loss_tangent": self.loss_tangent,
|
|
682
|
+
"permittivity": self.permittivity,
|
|
683
|
+
"conductivity": self.conductivity,
|
|
684
|
+
"zones": self.zones,
|
|
685
|
+
"transparency": self.transparency,
|
|
686
|
+
"name": self.name,
|
|
687
|
+
"roughness_enabled": self.roughness_enabled,
|
|
688
|
+
"thickness": self.thickness,
|
|
689
|
+
"lower_elevation": self.lower_elevation,
|
|
690
|
+
"upper_elevation": self.upper_elevation,
|
|
691
|
+
"type": self.type,
|
|
692
|
+
"top_hallhuray_nodule_radius": self.top_hallhuray_nodule_radius,
|
|
693
|
+
"top_hallhuray_surface_ratio": self.top_hallhuray_surface_ratio,
|
|
694
|
+
"side_hallhuray_nodule_radius": self.side_hallhuray_nodule_radius,
|
|
695
|
+
"side_hallhuray_surface_ratio": self.side_hallhuray_surface_ratio,
|
|
696
|
+
"bottom_hallhuray_nodule_radius": self.bottom_hallhuray_nodule_radius,
|
|
697
|
+
"bottom_hallhuray_surface_ratio": self.bottom_hallhuray_surface_ratio,
|
|
698
|
+
}
|
|
699
|
+
return dict_out
|
|
700
|
+
|
|
701
|
+
def _load_layer(self, layer):
|
|
702
|
+
if layer:
|
|
703
|
+
self.color = layer["color"]
|
|
704
|
+
self.type = layer["type"]
|
|
705
|
+
if isinstance(layer["material"], str):
|
|
706
|
+
self.material = layer["material"]
|
|
707
|
+
else:
|
|
708
|
+
material_data = layer["material"]
|
|
709
|
+
if material_data is not None:
|
|
710
|
+
material_name = layer["material"]["name"]
|
|
711
|
+
self._pedb.materials.add_material(material_name, **material_data)
|
|
712
|
+
self.material = material_name
|
|
713
|
+
if layer["dielectric_fill"]:
|
|
714
|
+
if isinstance(layer["dielectric_fill"], str):
|
|
715
|
+
self.dielectric_fill = layer["dielectric_fill"]
|
|
716
|
+
else:
|
|
717
|
+
dielectric_data = layer["dielectric_fill"]
|
|
718
|
+
if dielectric_data is not None:
|
|
719
|
+
self._pedb.materials.add_material(**dielectric_data)
|
|
720
|
+
self.dielectric_fill = layer["dielectric_fill"]["name"]
|
|
721
|
+
self.thickness = layer["thickness"]
|
|
722
|
+
self.etch_factor = layer["etch_factor"]
|
|
723
|
+
self.roughness_enabled = layer["roughness_enabled"]
|
|
724
|
+
if self.roughness_enabled:
|
|
725
|
+
self.top_hallhuray_nodule_radius = layer["top_hallhuray_nodule_radius"]
|
|
726
|
+
self.top_hallhuray_surface_ratio = layer["top_hallhuray_surface_ratio"]
|
|
727
|
+
self.assign_roughness_model(
|
|
728
|
+
"huray",
|
|
729
|
+
layer["top_hallhuray_nodule_radius"],
|
|
730
|
+
layer["top_hallhuray_surface_ratio"],
|
|
731
|
+
apply_on_surface="top",
|
|
732
|
+
)
|
|
733
|
+
self.bottom_hallhuray_nodule_radius = layer["bottom_hallhuray_nodule_radius"]
|
|
734
|
+
self.bottom_hallhuray_surface_ratio = layer["bottom_hallhuray_surface_ratio"]
|
|
735
|
+
self.assign_roughness_model(
|
|
736
|
+
"huray",
|
|
737
|
+
layer["bottom_hallhuray_nodule_radius"],
|
|
738
|
+
layer["bottom_hallhuray_surface_ratio"],
|
|
739
|
+
apply_on_surface="bottom",
|
|
740
|
+
)
|
|
741
|
+
self.side_hallhuray_nodule_radius = layer["side_hallhuray_nodule_radius"]
|
|
742
|
+
self.side_hallhuray_surface_ratio = layer["side_hallhuray_surface_ratio"]
|
|
743
|
+
self.assign_roughness_model(
|
|
744
|
+
"huray",
|
|
745
|
+
layer["side_hallhuray_nodule_radius"],
|
|
746
|
+
layer["side_hallhuray_surface_ratio"],
|
|
747
|
+
apply_on_surface="side",
|
|
748
|
+
)
|
|
@@ -37,7 +37,7 @@ class LayoutValidation:
|
|
|
37
37
|
self._pedb = pedb
|
|
38
38
|
self._layout_instance = self._pedb.layout_instance
|
|
39
39
|
|
|
40
|
-
def dc_shorts(self, net_list=None, fix=False):
|
|
40
|
+
def dc_shorts(self, net_list=None, fix=False) -> list[list[str, str]]:
|
|
41
41
|
"""Find DC shorts on layout.
|
|
42
42
|
|
|
43
43
|
Parameters
|
|
@@ -130,7 +130,7 @@ class LayoutValidation:
|
|
|
130
130
|
clean_disjoints_less_than=0.0,
|
|
131
131
|
order_by_area=False,
|
|
132
132
|
keep_disjoint_pins=False,
|
|
133
|
-
):
|
|
133
|
+
) -> list[str]:
|
|
134
134
|
"""Find and fix disjoint nets from a given netlist.
|
|
135
135
|
|
|
136
136
|
Parameters
|
|
@@ -263,7 +263,7 @@ class LayoutValidation:
|
|
|
263
263
|
|
|
264
264
|
return new_nets
|
|
265
265
|
|
|
266
|
-
def fix_self_intersections(self, net_list=None):
|
|
266
|
+
def fix_self_intersections(self, net_list=None) -> bool:
|
|
267
267
|
"""Find and fix self intersections from a given netlist.
|
|
268
268
|
|
|
269
269
|
Parameters
|
|
@@ -306,7 +306,7 @@ class LayoutValidation:
|
|
|
306
306
|
self._pedb._logger.info("Found {} illegal net names.".format(len(renamed_nets)))
|
|
307
307
|
return
|
|
308
308
|
|
|
309
|
-
def illegal_rlc_values(self, fix=False):
|
|
309
|
+
def illegal_rlc_values(self, fix=False) -> list[str]:
|
|
310
310
|
"""Find and fix RLC illegal values."""
|
|
311
311
|
inductors = self._pedb.components.inductors
|
|
312
312
|
|
|
@@ -318,7 +318,7 @@ class LayoutValidation:
|
|
|
318
318
|
if fix:
|
|
319
319
|
v.rlc_values = [0, 1, 0]
|
|
320
320
|
self._pedb._logger.info(f"Found {len(temp)} inductors have no value.")
|
|
321
|
-
return
|
|
321
|
+
return temp
|
|
322
322
|
|
|
323
323
|
def padstacks_no_name(self, fix=False):
|
|
324
324
|
pds = self._pedb.layout.padstack_instances
|
|
@@ -62,7 +62,7 @@ class Path(GrpcPath, Primitive):
|
|
|
62
62
|
"""
|
|
63
63
|
center_line_arcs = self._edb_object.cast().center_line.arc_data
|
|
64
64
|
path_length = 0.0
|
|
65
|
-
for arc in center_line_arcs
|
|
65
|
+
for arc in center_line_arcs:
|
|
66
66
|
path_length += arc.length
|
|
67
67
|
end_cap_style = self.get_end_cap_style()
|
|
68
68
|
if end_cap_style:
|
|
@@ -154,6 +154,45 @@ class SourceExcitation:
|
|
|
154
154
|
)
|
|
155
155
|
return True
|
|
156
156
|
|
|
157
|
+
def create_port(self, terminal, ref_terminal=None, is_circuit_port=False, name=None):
|
|
158
|
+
"""Create a port.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
terminal : class:`pyedb.dotnet.database.edb_data.terminals.EdgeTerminal`,
|
|
163
|
+
class:`pyedb.grpc.database.terminals.PadstackInstanceTerminal`,
|
|
164
|
+
class:`pyedb.grpc.database.terminals.PointTerminal`,
|
|
165
|
+
class:`pyedb.grpc.database.terminals.PinGroupTerminal`,
|
|
166
|
+
Positive terminal of the port.
|
|
167
|
+
ref_terminal : class:`pyedb.grpc.database.terminals.EdgeTerminal`,
|
|
168
|
+
class:`pyedb.grpc.database.terminals.PadstackInstanceTerminal`,
|
|
169
|
+
class:`pyedb.grpc.database.terminals.PointTerminal`,
|
|
170
|
+
class:`pyedb.grpc.database.terminals.PinGroupTerminal`,
|
|
171
|
+
optional
|
|
172
|
+
Negative terminal of the port.
|
|
173
|
+
is_circuit_port : bool, optional
|
|
174
|
+
Whether it is a circuit port. The default is ``False``.
|
|
175
|
+
name: str, optional
|
|
176
|
+
Name of the created port. The default is None, a random name is generated.
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
list: [:class:`GapPort <pyedb.grpc.database.ports.ports.GapPort`>,
|
|
180
|
+
:class:`WavePort <pyedb.grpc.database.ports.ports.WavePort>`].
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
from ansys.edb.core.terminal.terminal import BoundaryType as GrpcBoundaryType
|
|
184
|
+
|
|
185
|
+
if terminal.boundary_type == "port":
|
|
186
|
+
terminal.boundary_type = GrpcBoundaryType.PORT
|
|
187
|
+
terminal.is_circuit_port = is_circuit_port
|
|
188
|
+
if ref_terminal:
|
|
189
|
+
if ref_terminal.boundary_type == "port":
|
|
190
|
+
ref_terminal.boundary_type = GrpcBoundaryType.PORT
|
|
191
|
+
terminal.reference_terminal = ref_terminal
|
|
192
|
+
if name:
|
|
193
|
+
terminal.name = name
|
|
194
|
+
return self._pedb.ports[terminal.name]
|
|
195
|
+
|
|
157
196
|
def create_port_on_pins(
|
|
158
197
|
self,
|
|
159
198
|
refdes,
|
|
@@ -2098,6 +2137,30 @@ class SourceExcitation:
|
|
|
2098
2137
|
terms = [term for term in self._pedb.layout.terminals]
|
|
2099
2138
|
return len([i for i in terms if not i.is_reference_terminal])
|
|
2100
2139
|
|
|
2140
|
+
def get_point_terminal(self, name, net_name, location, layer) -> PointTerminal:
|
|
2141
|
+
"""Place terminal between two points.
|
|
2142
|
+
|
|
2143
|
+
Parameters
|
|
2144
|
+
----------
|
|
2145
|
+
name : str,
|
|
2146
|
+
Name of the terminal.
|
|
2147
|
+
net_name : str
|
|
2148
|
+
Name of the net.
|
|
2149
|
+
location : list
|
|
2150
|
+
Location of the terminal.
|
|
2151
|
+
layer : str,
|
|
2152
|
+
Layer of the terminal.
|
|
2153
|
+
|
|
2154
|
+
Returns
|
|
2155
|
+
-------
|
|
2156
|
+
:class:`PointTerminal <pyedb.grpc.database.terminal.point_terminal.PointTerminal>`
|
|
2157
|
+
"""
|
|
2158
|
+
from pyedb.grpc.database.terminal.point_terminal import PointTerminal
|
|
2159
|
+
|
|
2160
|
+
return PointTerminal.create(
|
|
2161
|
+
layout=self._pedb.active_layout, name=name, net=net_name, layer=layer, point=location
|
|
2162
|
+
)
|
|
2163
|
+
|
|
2101
2164
|
def create_rlc_boundary_on_pins(self, positive_pin=None, negative_pin=None, rvalue=0.0, lvalue=0.0, cvalue=0.0):
|
|
2102
2165
|
"""Create hfss rlc boundary on pins.
|
|
2103
2166
|
|
|
@@ -2328,6 +2391,37 @@ class SourceExcitation:
|
|
|
2328
2391
|
return terms
|
|
2329
2392
|
return False
|
|
2330
2393
|
|
|
2394
|
+
def create_current_source(self, terminal, ref_terminal):
|
|
2395
|
+
"""Create a current source.
|
|
2396
|
+
|
|
2397
|
+
Parameters
|
|
2398
|
+
----------
|
|
2399
|
+
terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2400
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PadstackInstanceTerminal>`,
|
|
2401
|
+
:class:`PointTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2402
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2403
|
+
Positive terminal of the source.
|
|
2404
|
+
ref_terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2405
|
+
:class:`pyedb.grpc.database.terminals.PadstackInstanceTerminal`,
|
|
2406
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2407
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2408
|
+
Negative terminal of the source.
|
|
2409
|
+
|
|
2410
|
+
Returns
|
|
2411
|
+
-------
|
|
2412
|
+
:class:`ExcitationSources <legacy.database.edb_data.ports.ExcitationSources>`
|
|
2413
|
+
"""
|
|
2414
|
+
from pyedb.grpc.database.terminal.terminal import Terminal
|
|
2415
|
+
|
|
2416
|
+
term = Terminal(self._pedb, terminal)
|
|
2417
|
+
term.boundary_type = "current_source"
|
|
2418
|
+
|
|
2419
|
+
ref_term = Terminal(self._pedb, ref_terminal)
|
|
2420
|
+
ref_term.boundary_type = "current_source"
|
|
2421
|
+
|
|
2422
|
+
term.ref_terminal = ref_terminal
|
|
2423
|
+
return term
|
|
2424
|
+
|
|
2331
2425
|
def create_current_source_on_pin_group(
|
|
2332
2426
|
self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None
|
|
2333
2427
|
):
|
|
@@ -2368,6 +2462,37 @@ class SourceExcitation:
|
|
|
2368
2462
|
pos_terminal.reference_terminal = neg_terminal
|
|
2369
2463
|
return True
|
|
2370
2464
|
|
|
2465
|
+
def create_voltage_source(self, terminal, ref_terminal):
|
|
2466
|
+
"""Create a voltage source.
|
|
2467
|
+
|
|
2468
|
+
Parameters
|
|
2469
|
+
----------
|
|
2470
|
+
terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2471
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PadstackInstanceTerminal>`,
|
|
2472
|
+
:class:`PointTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2473
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2474
|
+
Positive terminal of the source.
|
|
2475
|
+
ref_terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2476
|
+
:class:`pyedb.grpc.database.terminals.PadstackInstanceTerminal`,
|
|
2477
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2478
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2479
|
+
Negative terminal of the source.
|
|
2480
|
+
|
|
2481
|
+
Returns
|
|
2482
|
+
-------
|
|
2483
|
+
class:`ExcitationSources <legacy.database.edb_data.ports.ExcitationSources>`
|
|
2484
|
+
"""
|
|
2485
|
+
from pyedb.grpc.database.terminal.terminal import Terminal
|
|
2486
|
+
|
|
2487
|
+
term = Terminal(self._pedb, terminal)
|
|
2488
|
+
term.boundary_type = "voltage_source"
|
|
2489
|
+
|
|
2490
|
+
ref_term = Terminal(self._pedb, ref_terminal)
|
|
2491
|
+
ref_term.boundary_type = "voltage_source"
|
|
2492
|
+
|
|
2493
|
+
term.ref_terminal = ref_terminal
|
|
2494
|
+
return term
|
|
2495
|
+
|
|
2371
2496
|
def create_voltage_source_on_pin_group(
|
|
2372
2497
|
self, pos_pin_group_name, neg_pin_group_name, magnitude=1, phase=0, name=None, impedance=0.001
|
|
2373
2498
|
):
|
|
@@ -2408,6 +2533,37 @@ class SourceExcitation:
|
|
|
2408
2533
|
pos_terminal.reference_terminal = neg_terminal
|
|
2409
2534
|
return True
|
|
2410
2535
|
|
|
2536
|
+
def create_voltage_probe(self, terminal, ref_terminal):
|
|
2537
|
+
"""Create a voltage probe.
|
|
2538
|
+
|
|
2539
|
+
Parameters
|
|
2540
|
+
----------
|
|
2541
|
+
terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2542
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PadstackInstanceTerminal>`,
|
|
2543
|
+
:class:`PointTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2544
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2545
|
+
Positive terminal of the port.
|
|
2546
|
+
ref_terminal : :class:`EdgeTerminal <pyedb.grpc.database.terminals.EdgeTerminal>`,
|
|
2547
|
+
:class:`pyedb.grpc.database.terminals.PadstackInstanceTerminal`,
|
|
2548
|
+
:class:`PadstackInstanceTerminal <pyedb.grpc.database.terminals.PointTerminal>`,
|
|
2549
|
+
:class:`PinGroupTerminal <pyedb.grpc.database.terminals.PinGroupTerminal>`,
|
|
2550
|
+
Negative terminal of the probe.
|
|
2551
|
+
|
|
2552
|
+
Returns
|
|
2553
|
+
-------
|
|
2554
|
+
:class:`Terminal <pyedb.dotnet.database.edb_data.terminals.Terminal>`
|
|
2555
|
+
"""
|
|
2556
|
+
from pyedb.grpc.database.terminal.terminal import Terminal
|
|
2557
|
+
|
|
2558
|
+
term = Terminal(self._pedb, terminal)
|
|
2559
|
+
term.boundary_type = "voltage_probe"
|
|
2560
|
+
|
|
2561
|
+
ref_term = Terminal(self._pedb, ref_terminal)
|
|
2562
|
+
ref_term.boundary_type = "voltage_probe"
|
|
2563
|
+
|
|
2564
|
+
term.ref_terminal = ref_terminal
|
|
2565
|
+
return term
|
|
2566
|
+
|
|
2411
2567
|
def create_voltage_probe_on_pin_group(self, probe_name, pos_pin_group_name, neg_pin_group_name, impedance=1000000):
|
|
2412
2568
|
"""Create voltage probe between two pin groups.
|
|
2413
2569
|
|
pyedb/grpc/database/stackup.py
CHANGED
|
@@ -49,7 +49,6 @@ from ansys.edb.core.layer.layer_collection import LayerCollection as GrpcLayerCo
|
|
|
49
49
|
from ansys.edb.core.layer.layer_collection import LayerTypeSet as GrpcLayerTypeSet
|
|
50
50
|
from ansys.edb.core.layer.stackup_layer import StackupLayer as GrpcStackupLayer
|
|
51
51
|
from ansys.edb.core.layout.mcad_model import McadModel as GrpcMcadModel
|
|
52
|
-
from ansys.edb.core.utility.transform3d import Transform3D as GrpcTransform3D
|
|
53
52
|
from ansys.edb.core.utility.value import Value as GrpcValue
|
|
54
53
|
|
|
55
54
|
from pyedb.generic.general_methods import ET, generate_unique_name
|
|
@@ -312,6 +311,14 @@ class Stackup(LayerCollection):
|
|
|
312
311
|
super().__init__(pedb, edb_object)
|
|
313
312
|
self._pedb = pedb
|
|
314
313
|
|
|
314
|
+
def __getitem__(self, item):
|
|
315
|
+
if item in self.non_stackup_layers:
|
|
316
|
+
return Layer(self._pedb, self.find_by_name(item))
|
|
317
|
+
elif item in self.layers:
|
|
318
|
+
return StackupLayer(self._pedb, self.find_by_name(item))
|
|
319
|
+
else:
|
|
320
|
+
return None
|
|
321
|
+
|
|
315
322
|
@property
|
|
316
323
|
def _logger(self):
|
|
317
324
|
return self._pedb.logger
|
|
@@ -479,11 +486,16 @@ class Stackup(LayerCollection):
|
|
|
479
486
|
|
|
480
487
|
@mode.setter
|
|
481
488
|
def mode(self, value):
|
|
482
|
-
if value == 0 or value == GrpcLayerCollectionMode.LAMINATE or value == "laminate":
|
|
489
|
+
if value == 0 or value == GrpcLayerCollectionMode.LAMINATE or value == "laminate" or value == "Laminate":
|
|
483
490
|
super(LayerCollection, self.__class__).mode.__set__(self, GrpcLayerCollectionMode.LAMINATE)
|
|
484
|
-
elif
|
|
491
|
+
elif (
|
|
492
|
+
value == 1
|
|
493
|
+
or value == GrpcLayerCollectionMode.OVERLAPPING
|
|
494
|
+
or value == "overlapping"
|
|
495
|
+
or value == "Overlapping"
|
|
496
|
+
):
|
|
485
497
|
super(LayerCollection, self.__class__).mode.__set__(self, GrpcLayerCollectionMode.OVERLAPPING)
|
|
486
|
-
elif value == 2 or value == GrpcLayerCollectionMode.MULTIZONE or value == "multizone":
|
|
498
|
+
elif value == 2 or value == GrpcLayerCollectionMode.MULTIZONE or value == "multizone" or value == "MultiZone":
|
|
487
499
|
super(LayerCollection, self.__class__).mode.__set__(self, GrpcLayerCollectionMode.MULTIZONE)
|
|
488
500
|
self.update_layout()
|
|
489
501
|
|
|
@@ -696,6 +708,7 @@ class Stackup(LayerCollection):
|
|
|
696
708
|
else:
|
|
697
709
|
new_layer = self._create_nonstackup_layer(layer_name, layer_type)
|
|
698
710
|
self._set_layout_stackup(new_layer, "non_stackup")
|
|
711
|
+
return self.non_stackup_layers[layer_name]
|
|
699
712
|
return self.layers[layer_name]
|
|
700
713
|
|
|
701
714
|
def remove_layer(self, name):
|
|
@@ -819,9 +832,6 @@ class Stackup(LayerCollection):
|
|
|
819
832
|
layers_out = {}
|
|
820
833
|
for k, v in self.layers.items():
|
|
821
834
|
data = v._json_format()
|
|
822
|
-
# FIXME: Update the API to avoid providing following information to our users
|
|
823
|
-
del data["pedb"]
|
|
824
|
-
del data["edb_object"]
|
|
825
835
|
layers_out[k] = data
|
|
826
836
|
if v.material in self._pedb.materials.materials:
|
|
827
837
|
layer_material = self._pedb.materials.materials[v.material]
|
|
@@ -1039,10 +1049,14 @@ class Stackup(LayerCollection):
|
|
|
1039
1049
|
return round(thickness, 7)
|
|
1040
1050
|
|
|
1041
1051
|
def _get_solder_height(self, layer_name):
|
|
1052
|
+
height = 0.0
|
|
1042
1053
|
for _, val in self._pedb.components.instances.items():
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1054
|
+
try:
|
|
1055
|
+
if val.solder_ball_height and val.placement_layer == layer_name:
|
|
1056
|
+
height = val.solder_ball_height
|
|
1057
|
+
except:
|
|
1058
|
+
pass
|
|
1059
|
+
return height
|
|
1046
1060
|
|
|
1047
1061
|
def _remove_solder_pec(self, layer_name):
|
|
1048
1062
|
for _, val in self._pedb.components.instances.items():
|
|
@@ -1052,7 +1066,7 @@ class Stackup(LayerCollection):
|
|
|
1052
1066
|
port_property.reference_size_auto = False
|
|
1053
1067
|
port_property.reference_size = (GrpcValue(0.0), GrpcValue(0.0))
|
|
1054
1068
|
comp_prop.port_property = port_property
|
|
1055
|
-
val.
|
|
1069
|
+
val.component_property = comp_prop
|
|
1056
1070
|
|
|
1057
1071
|
def adjust_solder_dielectrics(self):
|
|
1058
1072
|
"""Adjust the stack-up by adding or modifying dielectric layers that contains Solder Balls.
|
|
@@ -1260,7 +1274,7 @@ class Stackup(LayerCollection):
|
|
|
1260
1274
|
_offset_y = GrpcValue(offset_y)
|
|
1261
1275
|
|
|
1262
1276
|
if edb_cell.name not in self._pedb.cell_names:
|
|
1263
|
-
list_cells = self._pedb.copy_cells(edb_cell
|
|
1277
|
+
list_cells = self._pedb.copy_cells(edb_cell)
|
|
1264
1278
|
edb_cell = list_cells[0]
|
|
1265
1279
|
self._pedb.layout.cell.is_blackbox = True
|
|
1266
1280
|
cell_inst2 = GrpcCellInstance.create(
|
|
@@ -1271,9 +1285,9 @@ class Stackup(LayerCollection):
|
|
|
1271
1285
|
stackup_source = self._pedb.layout.layer_collection
|
|
1272
1286
|
|
|
1273
1287
|
if place_on_top:
|
|
1274
|
-
cell_inst2.placement_layer = stackup_target.
|
|
1288
|
+
cell_inst2.placement_layer = list(LayerCollection(self._pedb, stackup_target).layers.values())[0]
|
|
1275
1289
|
else:
|
|
1276
|
-
cell_inst2.placement_layer = stackup_target.
|
|
1290
|
+
cell_inst2.placement_layer = list(LayerCollection(self._pedb, stackup_target).layers.values())[-1]
|
|
1277
1291
|
cell_inst2.placement_3d = True
|
|
1278
1292
|
res = stackup_target.get_top_bottom_stackup_layers(GrpcLayerTypeSet.SIGNAL_LAYER_SET)
|
|
1279
1293
|
target_top_elevation = res[1]
|
|
@@ -1301,7 +1315,13 @@ class Stackup(LayerCollection):
|
|
|
1301
1315
|
point_loc = GrpcPoint3DData(zero_data, zero_data, zero_data)
|
|
1302
1316
|
point_from = GrpcPoint3DData(one_data, zero_data, zero_data)
|
|
1303
1317
|
point_to = GrpcPoint3DData(math.cos(_angle), -1 * math.sin(_angle), zero_data)
|
|
1304
|
-
cell_inst2.transform3d
|
|
1318
|
+
transform = cell_inst2.transform3d.create_from_one_axis_to_another(from_axis=point_from, to_axis=point_to)
|
|
1319
|
+
cell_inst2.transform3d = transform
|
|
1320
|
+
transform = cell_inst2.transform3d.create_from_axis_and_angle(axis=point_loc, angle=angle)
|
|
1321
|
+
cell_inst2.transform3d = transform
|
|
1322
|
+
transform = cell_inst2.transform3d.create_from_offset(offset=point3d_t)
|
|
1323
|
+
cell_inst2.transform3d = transform
|
|
1324
|
+
# TODO check if component is properly placed.
|
|
1305
1325
|
return True
|
|
1306
1326
|
|
|
1307
1327
|
def place_instance(
|
|
@@ -1392,7 +1412,7 @@ class Stackup(LayerCollection):
|
|
|
1392
1412
|
_offset_y = GrpcValue(offset_y)
|
|
1393
1413
|
|
|
1394
1414
|
if edb_cell.name not in self._pedb.cell_names:
|
|
1395
|
-
list_cells = self._pedb.copy_cells(edb_cell
|
|
1415
|
+
list_cells = self._pedb.copy_cells(edb_cell)
|
|
1396
1416
|
edb_cell = list_cells[0]
|
|
1397
1417
|
for cell in self._pedb.active_db.top_circuit_cells:
|
|
1398
1418
|
if cell.name == edb_cell.name:
|
|
@@ -1444,7 +1464,13 @@ class Stackup(LayerCollection):
|
|
|
1444
1464
|
point_loc = GrpcPoint3DData(zero_data, zero_data, zero_data)
|
|
1445
1465
|
point_from = GrpcPoint3DData(one_data, zero_data, zero_data)
|
|
1446
1466
|
point_to = GrpcPoint3DData(math.cos(_angle), -1 * math.sin(_angle), zero_data)
|
|
1447
|
-
cell_inst2.transform3d
|
|
1467
|
+
transform = cell_inst2.transform3d.create_from_axis_and_angle(axis=point_loc, angle=angle)
|
|
1468
|
+
cell_inst2.transform3d = transform
|
|
1469
|
+
transform = cell_inst2.transform3d.create_from_one_axis_to_another(point_from, point_to)
|
|
1470
|
+
cell_inst2.transform3d = transform
|
|
1471
|
+
transform = cell_inst2.transform3d.create_from_offset(point3d_t)
|
|
1472
|
+
cell_inst2.transform3d = transform
|
|
1473
|
+
# TODO check is position is correct.
|
|
1448
1474
|
return cell_inst2
|
|
1449
1475
|
|
|
1450
1476
|
def place_a3dcomp_3d_placement(
|
|
@@ -1455,7 +1481,7 @@ class Stackup(LayerCollection):
|
|
|
1455
1481
|
offset_y=0.0,
|
|
1456
1482
|
offset_z=0.0,
|
|
1457
1483
|
place_on_top=True,
|
|
1458
|
-
):
|
|
1484
|
+
) -> bool:
|
|
1459
1485
|
"""Place a 3D Component into current layout.
|
|
1460
1486
|
3D Component ports are not visible via EDB. They will be visible after the EDB has been opened in Ansys
|
|
1461
1487
|
Electronics Desktop as a project.
|
|
@@ -1492,14 +1518,11 @@ class Stackup(LayerCollection):
|
|
|
1492
1518
|
... offset_y="2mm", flipped_stackup=False, place_on_top=True,
|
|
1493
1519
|
... )
|
|
1494
1520
|
"""
|
|
1495
|
-
zero_data = GrpcValue(0.0)
|
|
1496
|
-
one_data = GrpcValue(1.0)
|
|
1497
|
-
local_origin = GrpcPoint3DData(0.0, 0.0, 0.0)
|
|
1498
1521
|
rotation_axis_from = GrpcPoint3DData(1.0, 0.0, 0.0)
|
|
1499
1522
|
_angle = angle * math.pi / 180.0
|
|
1500
1523
|
rotation_axis_to = GrpcPoint3DData(math.cos(_angle), -1 * math.sin(_angle), 0.0)
|
|
1501
1524
|
|
|
1502
|
-
stackup_target =
|
|
1525
|
+
stackup_target = LayerCollection(self._pedb, self._pedb.layout.layer_collection)
|
|
1503
1526
|
res = stackup_target.get_top_bottom_stackup_layers(GrpcLayerTypeSet.SIGNAL_LAYER_SET)
|
|
1504
1527
|
target_top_elevation = res[1]
|
|
1505
1528
|
target_bottom_elevation = res[3]
|
|
@@ -1521,9 +1544,12 @@ class Stackup(LayerCollection):
|
|
|
1521
1544
|
return False
|
|
1522
1545
|
|
|
1523
1546
|
mcad_model.cell_instance.placement_3d = True
|
|
1524
|
-
mcad_model.cell_instance.transform3d
|
|
1525
|
-
|
|
1547
|
+
transform_rotation = mcad_model.cell_instance.transform3d.create_from_axis_and_angle(
|
|
1548
|
+
axis=rotation_axis_from, angle=flip_angle.value
|
|
1526
1549
|
)
|
|
1550
|
+
mcad_model.cell_instance.transform3d = transform_rotation
|
|
1551
|
+
transform_translation = mcad_model.cell_instance.transform3d.create_from_offset(offset=location)
|
|
1552
|
+
mcad_model.cell_instance.transform3d = transform_translation
|
|
1527
1553
|
return True
|
|
1528
1554
|
|
|
1529
1555
|
def residual_copper_area_per_layer(self):
|