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.

Files changed (40) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_modeler.py +42 -11
  3. pyedb/configuration/cfg_ports_sources.py +9 -1
  4. pyedb/configuration/cfg_stackup.py +4 -4
  5. pyedb/dotnet/database/cell/hierarchy/component.py +64 -8
  6. pyedb/dotnet/database/cell/hierarchy/s_parameter_model.py +7 -0
  7. pyedb/dotnet/database/cell/terminal/terminal.py +3 -3
  8. pyedb/dotnet/database/components.py +68 -46
  9. pyedb/dotnet/database/definition/package_def.py +29 -5
  10. pyedb/dotnet/database/edb_data/padstacks_data.py +5 -5
  11. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  12. pyedb/dotnet/database/edb_data/variables.py +3 -3
  13. pyedb/dotnet/database/geometry/polygon_data.py +14 -1
  14. pyedb/dotnet/database/materials.py +16 -16
  15. pyedb/dotnet/database/modeler.py +50 -9
  16. pyedb/dotnet/database/padstack.py +7 -5
  17. pyedb/dotnet/database/sim_setup_data/data/settings.py +28 -0
  18. pyedb/dotnet/database/siwave.py +36 -32
  19. pyedb/dotnet/database/stackup.py +1 -0
  20. pyedb/dotnet/database/utilities/hfss_simulation_setup.py +5 -6
  21. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +3 -1
  22. pyedb/dotnet/edb.py +22 -20
  23. pyedb/extensions/__init__.py +0 -0
  24. pyedb/extensions/via_design_backend.py +681 -0
  25. pyedb/grpc/database/components.py +57 -48
  26. pyedb/grpc/database/definition/materials.py +33 -33
  27. pyedb/grpc/database/definitions.py +6 -4
  28. pyedb/grpc/database/hfss.py +2 -2
  29. pyedb/grpc/database/hierarchy/component.py +3 -2
  30. pyedb/grpc/database/layers/stackup_layer.py +119 -22
  31. pyedb/grpc/database/layout_validation.py +5 -5
  32. pyedb/grpc/database/primitive/path.py +1 -1
  33. pyedb/grpc/database/source_excitations.py +156 -0
  34. pyedb/grpc/database/stackup.py +50 -24
  35. pyedb/grpc/edb.py +115 -105
  36. {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/METADATA +1 -1
  37. {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/RECORD +39 -38
  38. pyedb/extensions/pre_layout_design_toolkit/via_design.py +0 -1151
  39. {pyedb-0.48.0.dist-info → pyedb-0.50.0.dist-info}/LICENSE +0 -0
  40. {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
- return self._pedb.materials[self.material].conductivity
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
- return self._pedb.materials[self.material].permittivity
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
- return self._pedb.materials[self.material].loss_tangent
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 None
345
+ return 0.0
331
346
  except:
332
- return None
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 None
373
+ return 0.0
359
374
  except:
360
- return None
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 None
401
+ return 0.0
387
402
  except:
388
- return None
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 None
429
+ return 0.0
415
430
  except:
416
- return None
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 None
457
+ return 0.0
443
458
  except:
444
- return None
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 None
484
+ return 0.0
470
485
  except:
471
- return None
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 None
512
+ return 0.0
498
513
  except:
499
- return None
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 None
540
+ return 0.0
526
541
  except:
527
- return None
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 None
568
+ return 0.0
554
569
  except:
555
- return None
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[: int(len(center_line_arcs) / 2)]:
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
 
@@ -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 value == 1 or value == GrpcLayerCollectionMode.OVERLAPPING or value == "overlapping":
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
- if val.solder_ball_height and val.placement_layer == layer_name:
1044
- return val.solder_ball_height
1045
- return 0
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.edbcomponent.component_property = comp_prop
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.api_object)
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.Layers(GrpcLayerTypeSet.SIGNAL_LAYER_SET)[0]
1288
+ cell_inst2.placement_layer = list(LayerCollection(self._pedb, stackup_target).layers.values())[0]
1275
1289
  else:
1276
- cell_inst2.placement_layer = stackup_target.Layers(GrpcLayerTypeSet.SIGNAL_LAYER_SET)[-1]
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 = GrpcTransform3D(point_loc, point_from, point_to, rotation, point3d_t) # TODO check
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.api_object)
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 = (point_loc, point_from, point_to, rotation, point3d_t) # TODO check
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 = GrpcLayerCollection(self._pedb.layout.layer_collection)
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 = GrpcTransform3D(
1525
- local_origin, rotation_axis_from, rotation_axis_to, flip_angle, location
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):