pyedb 0.17.0__py3-none-any.whl → 0.19.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 (29) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_data.py +8 -11
  3. pyedb/configuration/cfg_nets.py +14 -0
  4. pyedb/configuration/cfg_pin_groups.py +57 -20
  5. pyedb/configuration/cfg_ports_sources.py +248 -60
  6. pyedb/configuration/configuration.py +51 -17
  7. pyedb/dotnet/edb.py +92 -28
  8. pyedb/dotnet/edb_core/cell/layout.py +48 -1
  9. pyedb/dotnet/edb_core/cell/terminal/padstack_instance_terminal.py +10 -0
  10. pyedb/dotnet/edb_core/cell/terminal/pingroup_terminal.py +5 -0
  11. pyedb/dotnet/edb_core/cell/terminal/point_terminal.py +0 -11
  12. pyedb/dotnet/edb_core/cell/terminal/terminal.py +35 -1
  13. pyedb/dotnet/edb_core/components.py +74 -18
  14. pyedb/dotnet/edb_core/dotnet/primitive.py +9 -6
  15. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +8 -4
  16. pyedb/dotnet/edb_core/edb_data/ports.py +0 -18
  17. pyedb/dotnet/edb_core/edb_data/primitives_data.py +1 -1
  18. pyedb/dotnet/edb_core/padstack.py +10 -1
  19. pyedb/dotnet/edb_core/sim_setup_data/data/sim_setup_info.py +42 -3
  20. pyedb/dotnet/edb_core/sim_setup_data/data/simulation_settings.py +92 -158
  21. pyedb/dotnet/edb_core/sim_setup_data/data/siw_dc_ir_settings.py +22 -22
  22. pyedb/dotnet/edb_core/sim_setup_data/io/siwave.py +76 -76
  23. pyedb/dotnet/edb_core/utilities/hfss_simulation_setup.py +23 -94
  24. pyedb/dotnet/edb_core/utilities/simulation_setup.py +40 -38
  25. pyedb/dotnet/edb_core/utilities/siwave_simulation_setup.py +26 -17
  26. {pyedb-0.17.0.dist-info → pyedb-0.19.0.dist-info}/METADATA +8 -8
  27. {pyedb-0.17.0.dist-info → pyedb-0.19.0.dist-info}/RECORD +29 -29
  28. {pyedb-0.17.0.dist-info → pyedb-0.19.0.dist-info}/LICENSE +0 -0
  29. {pyedb-0.17.0.dist-info → pyedb-0.19.0.dist-info}/WHEEL +0 -0
@@ -65,14 +65,16 @@ class Configuration:
65
65
  """
66
66
  if isinstance(config_file, dict):
67
67
  data = config_file
68
- elif os.path.isfile(config_file):
69
- with open(config_file, "r") as f:
70
- if config_file.endswith(".json"):
71
- data = json.load(f)
72
- elif config_file.endswith(".toml"):
73
- data = toml.load(f)
74
- else: # pragma: no cover
75
- return False
68
+ else:
69
+ config_file = str(config_file)
70
+ if os.path.isfile(config_file):
71
+ with open(config_file, "r") as f:
72
+ if config_file.endswith(".json"):
73
+ data = json.load(f)
74
+ elif config_file.endswith(".toml"):
75
+ data = toml.load(f)
76
+ else: # pragma: no cover
77
+ return False
76
78
 
77
79
  if not append: # pragma: no cover
78
80
  self.data = {}
@@ -120,16 +122,13 @@ class Configuration:
120
122
  self.cfg_data.padstacks.apply()
121
123
 
122
124
  # Configure pin groups
123
- for pin_group in self.cfg_data.pin_groups:
124
- pin_group.apply()
125
+ self.cfg_data.pin_groups.apply()
125
126
 
126
127
  # Configure ports
127
- for port in self.cfg_data.ports:
128
- port.create()
128
+ self.cfg_data.ports.apply()
129
129
 
130
130
  # Configure sources
131
- for source in self.cfg_data.sources:
132
- source.create()
131
+ self.cfg_data.sources.apply()
133
132
 
134
133
  # Configure setup
135
134
  self.cfg_data.setups.apply()
@@ -271,12 +270,30 @@ class Configuration:
271
270
  data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
272
271
  if kwargs.get("setups", False):
273
272
  data["setups"] = self.cfg_data.setups.get_data_from_db()
273
+ if kwargs.get("sources", False):
274
+ data["sources"] = self.cfg_data.sources.get_data_from_db()
275
+ if kwargs.get("ports", False):
276
+ data["ports"] = self.cfg_data.ports.get_data_from_db()
274
277
  if kwargs.get("components", False):
275
278
  data["components"] = self.cfg_data.components.get_data_from_db()
279
+ if kwargs.get("nets", False):
280
+ data["nets"] = self.cfg_data.nets.get_data_from_db()
281
+ if kwargs.get("pin_groups", False):
282
+ data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db()
276
283
 
277
284
  return data
278
285
 
279
- def export(self, file_path, stackup=True, package_definitions=True, setups=True):
286
+ def export(
287
+ self,
288
+ file_path,
289
+ stackup=True,
290
+ package_definitions=True,
291
+ setups=True,
292
+ sources=True,
293
+ ports=True,
294
+ nets=True,
295
+ pin_groups=True,
296
+ ):
280
297
  """Export the configuration data from layout to a file.
281
298
 
282
299
  Parameters
@@ -285,14 +302,31 @@ class Configuration:
285
302
  File path to export the configuration data.
286
303
  stackup : bool
287
304
  Whether to export stackup or not.
288
-
305
+ package_definitions : bool
306
+ Whether to export package definitions or not.
307
+ setups : bool
308
+ Whether to export setups or not.
309
+ sources : bool
310
+ Whether to export sources or not.
311
+ ports : bool
312
+ Whether to export ports or not.
313
+ nets : bool
314
+ Whether to export nets.
289
315
  Returns
290
316
  -------
291
317
  bool
292
318
  """
293
319
  file_path = file_path if isinstance(file_path, Path) else Path(file_path)
294
320
  file_path = file_path if file_path.suffix == ".json" else file_path.with_suffix(".json")
295
- data = self.get_data_from_db(stackup=stackup, package_definitions=package_definitions, setups=setups)
321
+ data = self.get_data_from_db(
322
+ stackup=stackup,
323
+ package_definitions=package_definitions,
324
+ setups=setups,
325
+ sources=sources,
326
+ ports=ports,
327
+ nets=nets,
328
+ pin_groups=pin_groups,
329
+ )
296
330
  with open(file_path, "w") as f:
297
331
  json.dump(data, f, ensure_ascii=False, indent=4)
298
332
  return True if os.path.isfile(file_path) else False
pyedb/dotnet/edb.py CHANGED
@@ -510,6 +510,7 @@ class Edb(Database):
510
510
  def sources(self):
511
511
  """Get all layout sources."""
512
512
  terms = [term for term in self.layout.terminals if int(term.GetBoundaryType()) in [3, 4, 7]]
513
+ terms = [term for term in terms if not term.IsReferenceTerminal()]
513
514
  return {ter.GetName(): ExcitationSources(self, ter) for ter in terms}
514
515
 
515
516
  @property
@@ -1558,6 +1559,7 @@ class Edb(Database):
1558
1559
  reference_list=[],
1559
1560
  include_pingroups=True,
1560
1561
  pins_to_preserve=None,
1562
+ inlcude_voids_in_extents=False,
1561
1563
  ):
1562
1564
  if extent_type in [
1563
1565
  "Conforming",
@@ -1574,6 +1576,7 @@ class Edb(Database):
1574
1576
  smart_cut,
1575
1577
  reference_list,
1576
1578
  pins_to_preserve,
1579
+ inlcude_voids_in_extents=inlcude_voids_in_extents,
1577
1580
  )
1578
1581
  else:
1579
1582
  _poly = self.layout.expanded_extent(
@@ -1632,6 +1635,7 @@ class Edb(Database):
1632
1635
  smart_cutout=False,
1633
1636
  reference_list=[],
1634
1637
  pins_to_preserve=None,
1638
+ inlcude_voids_in_extents=False,
1635
1639
  ):
1636
1640
  names = []
1637
1641
  _polys = []
@@ -1649,7 +1653,7 @@ class Edb(Database):
1649
1653
 
1650
1654
  for prim in self.modeler.primitives:
1651
1655
  if prim is not None and prim.net_name in names:
1652
- _polys.append(prim.primitive_object.GetPolygonData())
1656
+ _polys.append(prim)
1653
1657
  if smart_cutout:
1654
1658
  objs_data = self._smart_cut(reference_list, expansion_size)
1655
1659
  _polys.extend(objs_data)
@@ -1658,9 +1662,33 @@ class Edb(Database):
1658
1662
  while k < 10:
1659
1663
  unite_polys = []
1660
1664
  for i in _polys:
1661
- obj_data = i.Expand(expansion_size, tolerance, round_corner, round_extension)
1665
+ if "PolygonData" not in str(i):
1666
+ obj_data = i.primitive_object.GetPolygonData().Expand(
1667
+ expansion_size, tolerance, round_corner, round_extension
1668
+ )
1669
+ else:
1670
+ obj_data = i.Expand(expansion_size, tolerance, round_corner, round_extension)
1662
1671
  if obj_data:
1663
- unite_polys.extend(list(obj_data))
1672
+ if not inlcude_voids_in_extents:
1673
+ unite_polys.extend(list(obj_data))
1674
+ else:
1675
+ voids_poly = []
1676
+ try:
1677
+ if i.HasVoids():
1678
+ area = i.area()
1679
+ for void in i.Voids:
1680
+ void_polydata = void.GetPolygonData()
1681
+ if void_polydata.Area() >= 0.05 * area:
1682
+ voids_poly.append(void_polydata)
1683
+ if voids_poly:
1684
+ obj_data = obj_data[0].Subtract(
1685
+ convert_py_list_to_net_list(list(obj_data)),
1686
+ convert_py_list_to_net_list(voids_poly),
1687
+ )
1688
+ except:
1689
+ pass
1690
+ finally:
1691
+ unite_polys.extend(list(obj_data))
1664
1692
  _poly_unite = self.edb_api.geometry.polygon_data.unite(unite_polys)
1665
1693
  if len(_poly_unite) == 1:
1666
1694
  self.logger.info("Correctly computed Extension at first iteration.")
@@ -1761,6 +1789,7 @@ class Edb(Database):
1761
1789
  preserve_components_with_model=False,
1762
1790
  simple_pad_check=True,
1763
1791
  keep_lines_as_path=False,
1792
+ include_voids_in_extents=False,
1764
1793
  ):
1765
1794
  """Create a cutout using an approach entirely based on PyAEDT.
1766
1795
  This method replaces all legacy cutout methods in PyAEDT.
@@ -1837,6 +1866,11 @@ class Edb(Database):
1837
1866
  This feature works only in Electronics Desktop (3D Layout).
1838
1867
  If the flag is set to ``True`` it can cause issues in SiWave once the Edb is imported.
1839
1868
  Default is ``False`` to generate PolygonData of cut lines.
1869
+ include_voids_in_extents : bool, optional
1870
+ Whether to compute and include voids in pyaedt extent before the cutout. Cutout time can be affected.
1871
+ It works only with Conforming cutout.
1872
+ Default is ``False`` to generate extent without voids.
1873
+
1840
1874
 
1841
1875
  Returns
1842
1876
  -------
@@ -1896,6 +1930,7 @@ class Edb(Database):
1896
1930
  use_pyaedt_extent_computing=use_pyaedt_extent_computing,
1897
1931
  check_terminals=check_terminals,
1898
1932
  include_pingroups=include_pingroups,
1933
+ inlcude_voids_in_extents=include_voids_in_extents,
1899
1934
  )
1900
1935
  else:
1901
1936
  legacy_path = self.edbpath
@@ -1929,6 +1964,7 @@ class Edb(Database):
1929
1964
  include_partial=include_partial_instances,
1930
1965
  simple_pad_check=simple_pad_check,
1931
1966
  keep_lines_as_path=keep_lines_as_path,
1967
+ inlcude_voids_in_extents=include_voids_in_extents,
1932
1968
  )
1933
1969
  if self.are_port_reference_terminals_connected():
1934
1970
  if output_aedb_path:
@@ -1969,6 +2005,7 @@ class Edb(Database):
1969
2005
  include_partial=include_partial_instances,
1970
2006
  simple_pad_check=simple_pad_check,
1971
2007
  keep_lines_as_path=keep_lines_as_path,
2008
+ inlcude_voids_in_extents=include_voids_in_extents,
1972
2009
  )
1973
2010
  if result and not open_cutout_at_end and self.edbpath != legacy_path:
1974
2011
  self.save_edb()
@@ -1990,6 +2027,7 @@ class Edb(Database):
1990
2027
  remove_single_pin_components=False,
1991
2028
  check_terminals=False,
1992
2029
  include_pingroups=True,
2030
+ inlcude_voids_in_extents=False,
1993
2031
  ):
1994
2032
  expansion_size = self.edb_value(expansion_size).ToDouble()
1995
2033
 
@@ -2010,8 +2048,14 @@ class Edb(Database):
2010
2048
  smart_cut=check_terminals,
2011
2049
  reference_list=reference_list,
2012
2050
  include_pingroups=include_pingroups,
2051
+ inlcude_voids_in_extents=inlcude_voids_in_extents,
2013
2052
  )
2014
-
2053
+ _poly1 = _poly.CreateFromArcs(_poly.GetArcData(), True)
2054
+ if inlcude_voids_in_extents:
2055
+ for hole in list(_poly.Holes):
2056
+ if hole.Area() >= 0.05 * _poly1.Area():
2057
+ _poly1.AddHole(hole)
2058
+ _poly = _poly1
2015
2059
  # Create new cutout cell/design
2016
2060
  included_nets_list = signal_list + reference_list
2017
2061
  included_nets = convert_py_list_to_net_list(
@@ -2172,6 +2216,7 @@ class Edb(Database):
2172
2216
  include_partial=False,
2173
2217
  simple_pad_check=True,
2174
2218
  keep_lines_as_path=False,
2219
+ inlcude_voids_in_extents=False,
2175
2220
  ):
2176
2221
  if is_ironpython: # pragma: no cover
2177
2222
  self.logger.error("Method working only in Cpython")
@@ -2269,11 +2314,18 @@ class Edb(Database):
2269
2314
  reference_list=reference_list,
2270
2315
  include_pingroups=include_pingroups,
2271
2316
  pins_to_preserve=pins_to_preserve,
2317
+ inlcude_voids_in_extents=inlcude_voids_in_extents,
2272
2318
  )
2273
2319
  if extent_type in ["Conforming", self.edb_api.geometry.extent_type.Conforming, 1]:
2274
2320
  if extent_defeature > 0:
2275
2321
  _poly = _poly.Defeature(extent_defeature)
2276
- _poly = _poly.CreateFromArcs(_poly.GetArcData(), True)
2322
+
2323
+ _poly1 = _poly.CreateFromArcs(_poly.GetArcData(), True)
2324
+ if inlcude_voids_in_extents:
2325
+ for hole in list(_poly.Holes):
2326
+ if hole.Area() >= 0.05 * _poly1.Area():
2327
+ _poly1.AddHole(hole)
2328
+ _poly = _poly1
2277
2329
  if not _poly or _poly.IsNull():
2278
2330
  self._logger.error("Failed to create Extent.")
2279
2331
  return []
@@ -2312,29 +2364,39 @@ class Edb(Database):
2312
2364
  pdata = prim_1.polygon_data.edb_api
2313
2365
  int_data = _poly.GetIntersectionType(pdata)
2314
2366
  if int_data == 2:
2315
- return
2367
+ if not inlcude_voids_in_extents:
2368
+ return
2369
+ skip = False
2370
+ for hole in list(_poly.Holes):
2371
+ if hole.GetIntersectionType(pdata) == 0:
2372
+ prims_to_delete.append(prim_1)
2373
+ return
2374
+ elif hole.GetIntersectionType(pdata) == 1:
2375
+ skip = True
2376
+ if skip:
2377
+ return
2316
2378
  elif int_data == 0:
2317
2379
  prims_to_delete.append(prim_1)
2318
- else:
2319
- list_poly = intersect(_poly, pdata)
2320
- if list_poly:
2321
- net = prim_1.net_name
2322
- voids = prim_1.voids
2323
- for p in list_poly:
2324
- if p.IsNull():
2325
- continue
2326
- # points = list(p.Points)
2327
- list_void = []
2328
- if voids:
2329
- voids_data = [void.polygon_data.edb_api for void in voids]
2330
- list_prims = subtract(p, voids_data)
2331
- for prim in list_prims:
2332
- if not prim.IsNull():
2333
- poly_to_create.append([prim, prim_1.layer.name, net, list_void])
2334
- else:
2335
- poly_to_create.append([p, prim_1.layer.name, net, list_void])
2380
+ return
2381
+ list_poly = intersect(_poly, pdata)
2382
+ if list_poly:
2383
+ net = prim_1.net_name
2384
+ voids = prim_1.voids
2385
+ for p in list_poly:
2386
+ if p.IsNull():
2387
+ continue
2388
+ # points = list(p.Points)
2389
+ list_void = []
2390
+ if voids:
2391
+ voids_data = [void.polygon_data.edb_api for void in voids]
2392
+ list_prims = subtract(p, voids_data)
2393
+ for prim in list_prims:
2394
+ if not prim.IsNull():
2395
+ poly_to_create.append([prim, prim_1.layer.name, net, list_void])
2396
+ else:
2397
+ poly_to_create.append([p, prim_1.layer.name, net, list_void])
2336
2398
 
2337
- prims_to_delete.append(prim_1)
2399
+ prims_to_delete.append(prim_1)
2338
2400
 
2339
2401
  def pins_clean(pinst):
2340
2402
  if not pinst.in_polygon(_poly, include_partial=include_partial, simple_check=simple_pad_check):
@@ -2350,7 +2412,9 @@ class Edb(Database):
2350
2412
  for pin in pins_to_delete:
2351
2413
  pin.delete()
2352
2414
 
2353
- self.logger.info_timer("Padstack Instances removal completed")
2415
+ self.logger.info_timer(
2416
+ "Padstack Instances removal completed. {} instances removed.".format(len(pins_to_delete))
2417
+ )
2354
2418
  self.logger.reset_timer()
2355
2419
 
2356
2420
  # with ThreadPoolExecutor(number_of_threads) as pool:
@@ -2369,7 +2433,7 @@ class Edb(Database):
2369
2433
  for prim in prims_to_delete:
2370
2434
  prim.delete()
2371
2435
 
2372
- self.logger.info_timer("Primitives cleanup completed")
2436
+ self.logger.info_timer("Primitives cleanup completed. {} primitives deleted.".format(len(prims_to_delete)))
2373
2437
  self.logger.reset_timer()
2374
2438
 
2375
2439
  i = 0
@@ -3680,7 +3744,7 @@ class Edb(Database):
3680
3744
  if float(self.edbversion) < 2024.2:
3681
3745
  self.logger.error("HFSSPI simulation only supported with Ansys release 2024R2 and higher")
3682
3746
  return False
3683
- return HFSSPISimulationSetup(self).create(name)
3747
+ return HFSSPISimulationSetup(self, name=name)
3684
3748
 
3685
3749
  def create_siwave_syz_setup(self, name=None, **kwargs):
3686
3750
  """Create a setup from a template.
@@ -23,9 +23,11 @@
23
23
  """
24
24
  This module contains these classes: `EdbLayout` and `Shape`.
25
25
  """
26
-
27
26
  from pyedb.dotnet.edb_core.cell.primitive import Bondwire
28
27
  from pyedb.dotnet.edb_core.edb_data.nets_data import EDBNetsData
28
+ from pyedb.dotnet.edb_core.edb_data.padstacks_data import EDBPadstackInstance
29
+ from pyedb.dotnet.edb_core.edb_data.sources import PinGroup
30
+ from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
29
31
  from pyedb.dotnet.edb_core.layout import EdbLayout
30
32
 
31
33
 
@@ -65,6 +67,11 @@ class Layout(EdbLayout):
65
67
  if i.GetPrimitiveType().ToString() == "Bondwire"
66
68
  ]
67
69
 
70
+ @property
71
+ def padstack_instances(self):
72
+ """Get all padstack instances in a list."""
73
+ return [EDBPadstackInstance(i, self._pedb) for i in self._edb_object.PadstackInstances]
74
+
68
75
  def create_bondwire(
69
76
  self,
70
77
  definition_name,
@@ -129,3 +136,43 @@ class Layout(EdbLayout):
129
136
  end_y=self._pedb.edb_value(end_y),
130
137
  net=self.nets[net]._edb_object,
131
138
  )
139
+
140
+ def find_object_by_id(self, value: int):
141
+ """Find a Connectable object by Database ID.
142
+
143
+ Parameters
144
+ ----------
145
+ value : int
146
+ """
147
+ obj = self._pedb._edb.Cell.Connectable.FindById(self._edb_object, value)
148
+ if obj.GetObjType().ToString() == "PadstackInstance":
149
+ return EDBPadstackInstance(obj, self._pedb)
150
+
151
+ def create_pin_group(self, name: str, pins_by_id: list[int] = None, pins_by_aedt_name: list[str] = None):
152
+ """Create a PinGroup.
153
+
154
+ Parameters
155
+ name : str,
156
+ Name of the PinGroup.
157
+ pins_by_id : list[int] or None
158
+ List of pins by ID.
159
+ pins_by_aedt_name : list[str] or None
160
+ List of pins by AEDT name.
161
+ """
162
+ pins = []
163
+
164
+ if pins_by_id is not None:
165
+ for p in pins_by_id:
166
+ pins.append(self.find_object_by_id(p._edb_object))
167
+ else:
168
+ p_inst = self.padstack_instances
169
+ while True:
170
+ p = p_inst.pop(0)
171
+ if p.aedt_name in pins_by_aedt_name:
172
+ pins.append(p._edb_object)
173
+ pins_by_aedt_name.remove(p.aedt_name)
174
+ if len(pins_by_aedt_name) == 0:
175
+ break
176
+
177
+ obj = self._edb.cell.hierarchy.pin_group.Create(self._edb_object, name, convert_py_list_to_net_list(pins))
178
+ return PinGroup(name, obj, self._pedb)
@@ -86,3 +86,13 @@ class PadstackInstanceTerminal(Terminal):
86
86
  terminal = PadstackInstanceTerminal(self._pedb, terminal)
87
87
 
88
88
  return terminal if not terminal.is_null else False
89
+
90
+ def _get_parameters(self):
91
+ """Gets the parameters of the padstack instance terminal."""
92
+ _, padstack_inst, layer_obj = self._edb_object.GetParameters()
93
+ return padstack_inst, layer_obj
94
+
95
+ @property
96
+ def padstack_instance(self):
97
+ p_inst, _ = self._get_parameters()
98
+ return self._pedb.modeler.find_object_by_id(p_inst.GetId())
@@ -57,3 +57,8 @@ class PinGroupTerminal(Terminal):
57
57
  )
58
58
  term = PinGroupTerminal(self._pedb, term)
59
59
  return term if not term.is_null else False
60
+
61
+ def pin_group(self):
62
+ """Gets the pin group the terminal refers to."""
63
+ name = self._edb_object.GetPinGroup().GetName()
64
+ return self._pedb.siwave.pin_groups[name]
@@ -60,14 +60,3 @@ class PointTerminal(Terminal):
60
60
  )
61
61
  terminal = PointTerminal(self._pedb, terminal)
62
62
  return terminal if not terminal.is_null else False
63
-
64
- @property
65
- def ref_terminal(self):
66
- """Get reference terminal."""
67
-
68
- terminal = Terminal(self._pedb, self._edb_object.GetReferenceTerminal())
69
- return terminal if not terminal.is_null else False
70
-
71
- @ref_terminal.setter
72
- def ref_terminal(self, value):
73
- self._edb_object.SetReferenceTerminal(value._edb_object)
@@ -217,6 +217,21 @@ class Terminal(Connectable):
217
217
  def boundary_type(self, value):
218
218
  self._edb_object.SetBoundaryType(self._boundary_type_mapping[value])
219
219
 
220
+ @property
221
+ def is_port(self):
222
+ """Whether it is a port."""
223
+ return True if self.boundary_type == "PortBoundary" else False
224
+
225
+ @property
226
+ def is_current_source(self):
227
+ """Whether it is a current source."""
228
+ return True if self.boundary_type == "kCurrentSource" else False
229
+
230
+ @property
231
+ def is_voltage_source(self):
232
+ """Whether it is a voltage source."""
233
+ return True if self.boundary_type == "kVoltageSource" else False
234
+
220
235
  @property
221
236
  def impedance(self):
222
237
  """Impedance of the port."""
@@ -235,7 +250,8 @@ class Terminal(Connectable):
235
250
  def ref_terminal(self):
236
251
  """Get reference terminal."""
237
252
 
238
- terminal = Terminal(self._pedb, self._edb_object.GetReferenceTerminal())
253
+ edb_terminal = self._edb_object.GetReferenceTerminal()
254
+ terminal = self._pedb.terminals[edb_terminal.GetName()]
239
255
  if not terminal.is_null:
240
256
  return terminal
241
257
 
@@ -444,3 +460,21 @@ class Terminal(Connectable):
444
460
  pin_obj = pin
445
461
  if pin_obj:
446
462
  return EDBPadstackInstance(pin_obj, self._pedb)
463
+
464
+ @property
465
+ def magnitude(self):
466
+ """Get the magnitude of the source."""
467
+ return self._edb_object.GetSourceAmplitude().ToDouble()
468
+
469
+ @magnitude.setter
470
+ def magnitude(self, value):
471
+ self._edb_object.SetSourceAmplitude(self._edb.utility.value(value))
472
+
473
+ @property
474
+ def phase(self):
475
+ """Get the phase of the source."""
476
+ return self._edb_object.GetSourcePhase().ToDouble()
477
+
478
+ @phase.setter
479
+ def phase(self, value):
480
+ self._edb_object.SetSourcePhase(self._edb.utility.value(value))