pyedb 0.56.0__py3-none-any.whl → 0.58.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 (110) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_data.py +3 -0
  3. pyedb/configuration/cfg_pin_groups.py +2 -0
  4. pyedb/configuration/cfg_terminals.py +232 -0
  5. pyedb/configuration/configuration.py +146 -3
  6. pyedb/dotnet/clr_module.py +1 -2
  7. pyedb/dotnet/database/Variables.py +30 -22
  8. pyedb/dotnet/database/cell/hierarchy/component.py +2 -8
  9. pyedb/dotnet/database/cell/layout.py +5 -1
  10. pyedb/dotnet/database/cell/primitive/primitive.py +2 -2
  11. pyedb/dotnet/database/cell/terminal/bundle_terminal.py +12 -0
  12. pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
  13. pyedb/dotnet/database/cell/terminal/terminal.py +38 -0
  14. pyedb/dotnet/database/components.py +15 -19
  15. pyedb/dotnet/database/dotnet/database.py +1 -0
  16. pyedb/dotnet/database/edb_data/control_file.py +19 -8
  17. pyedb/dotnet/database/edb_data/nets_data.py +3 -3
  18. pyedb/dotnet/database/edb_data/padstacks_data.py +39 -14
  19. pyedb/dotnet/database/edb_data/ports.py +0 -25
  20. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  21. pyedb/dotnet/database/edb_data/raptor_x_simulation_setup_data.py +18 -19
  22. pyedb/dotnet/database/edb_data/simulation_configuration.py +3 -3
  23. pyedb/dotnet/database/edb_data/sources.py +21 -2
  24. pyedb/dotnet/database/general.py +1 -6
  25. pyedb/dotnet/database/hfss.py +9 -8
  26. pyedb/dotnet/database/layout_validation.py +14 -3
  27. pyedb/dotnet/database/materials.py +1 -3
  28. pyedb/dotnet/database/modeler.py +7 -3
  29. pyedb/dotnet/database/nets.py +27 -19
  30. pyedb/dotnet/database/padstack.py +4 -2
  31. pyedb/dotnet/database/sim_setup_data/io/siwave.py +54 -1
  32. pyedb/dotnet/database/siwave.py +4 -3
  33. pyedb/dotnet/database/stackup.py +55 -58
  34. pyedb/dotnet/database/utilities/heatsink.py +0 -1
  35. pyedb/dotnet/database/utilities/hfss_simulation_setup.py +81 -0
  36. pyedb/dotnet/database/utilities/simulation_setup.py +7 -5
  37. pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +1 -0
  38. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +264 -13
  39. pyedb/dotnet/edb.py +65 -47
  40. pyedb/exceptions.py +1 -2
  41. pyedb/extensions/create_cell_array.py +67 -49
  42. pyedb/generic/data_handlers.py +13 -23
  43. pyedb/generic/design_types.py +9 -35
  44. pyedb/generic/filesystem.py +4 -2
  45. pyedb/generic/general_methods.py +28 -41
  46. pyedb/generic/plot.py +8 -23
  47. pyedb/generic/process.py +78 -10
  48. pyedb/grpc/database/_typing.py +0 -0
  49. pyedb/grpc/database/components.py +14 -13
  50. pyedb/grpc/database/control_file.py +27 -40
  51. pyedb/grpc/database/definition/materials.py +1 -1
  52. pyedb/grpc/database/definition/package_def.py +6 -3
  53. pyedb/grpc/database/definition/padstack_def.py +14 -12
  54. pyedb/grpc/database/hfss.py +1 -4
  55. pyedb/grpc/database/hierarchy/component.py +5 -13
  56. pyedb/grpc/database/hierarchy/pingroup.py +16 -3
  57. pyedb/grpc/database/layers/layer.py +1 -2
  58. pyedb/grpc/database/layers/stackup_layer.py +42 -19
  59. pyedb/grpc/database/layout/layout.py +43 -27
  60. pyedb/grpc/database/layout/voltage_regulator.py +6 -1
  61. pyedb/grpc/database/layout_validation.py +5 -2
  62. pyedb/grpc/database/modeler.py +254 -252
  63. pyedb/grpc/database/net/differential_pair.py +9 -2
  64. pyedb/grpc/database/net/extended_net.py +24 -9
  65. pyedb/grpc/database/net/net.py +14 -5
  66. pyedb/grpc/database/net/net_class.py +24 -7
  67. pyedb/grpc/database/nets.py +11 -43
  68. pyedb/grpc/database/padstacks.py +67 -119
  69. pyedb/grpc/database/primitive/bondwire.py +3 -67
  70. pyedb/grpc/database/primitive/circle.py +42 -3
  71. pyedb/grpc/database/primitive/padstack_instance.py +58 -31
  72. pyedb/grpc/database/primitive/path.py +160 -11
  73. pyedb/grpc/database/primitive/polygon.py +73 -7
  74. pyedb/grpc/database/primitive/primitive.py +2 -2
  75. pyedb/grpc/database/primitive/rectangle.py +105 -4
  76. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +0 -2
  77. pyedb/grpc/database/simulation_setup/hfss_settings_options.py +0 -4
  78. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +79 -0
  79. pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +1 -0
  80. pyedb/grpc/database/simulation_setup/sweep_data.py +1 -3
  81. pyedb/grpc/database/siwave.py +6 -13
  82. pyedb/grpc/database/source_excitations.py +46 -63
  83. pyedb/grpc/database/stackup.py +55 -60
  84. pyedb/grpc/database/terminal/bundle_terminal.py +10 -3
  85. pyedb/grpc/database/terminal/padstack_instance_terminal.py +9 -11
  86. pyedb/grpc/database/terminal/pingroup_terminal.py +8 -1
  87. pyedb/grpc/database/terminal/point_terminal.py +30 -0
  88. pyedb/grpc/database/terminal/terminal.py +35 -10
  89. pyedb/grpc/database/utility/heat_sink.py +0 -1
  90. pyedb/grpc/database/utility/hfss_extent_info.py +2 -2
  91. pyedb/grpc/database/utility/xml_control_file.py +19 -8
  92. pyedb/grpc/edb.py +63 -32
  93. pyedb/grpc/edb_init.py +1 -0
  94. pyedb/ipc2581/ecad/cad_data/layer_feature.py +6 -2
  95. pyedb/ipc2581/ecad/cad_data/step.py +1 -1
  96. pyedb/ipc2581/ipc2581.py +8 -7
  97. pyedb/libraries/common.py +3 -4
  98. pyedb/libraries/rf_libraries/base_functions.py +7 -16
  99. pyedb/libraries/rf_libraries/planar_antennas.py +3 -21
  100. pyedb/misc/aedtlib_personalib_install.py +2 -2
  101. pyedb/misc/downloads.py +19 -3
  102. pyedb/misc/misc.py +5 -2
  103. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +3 -2
  104. pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py +0 -1
  105. pyedb/misc/utilities.py +0 -1
  106. pyedb/modeler/geometry_operators.py +3 -2
  107. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/METADATA +6 -7
  108. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/RECORD +110 -108
  109. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/WHEEL +0 -0
  110. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/licenses/LICENSE +0 -0
@@ -155,7 +155,7 @@ class LayoutValidation:
155
155
  Examples
156
156
  --------
157
157
 
158
- >>> renamed_nets = edb.layout_validation.disjoint_nets(["GND","Net2"])
158
+ >>> renamed_nets = edb.layout_validation.disjoint_nets(["GND", "Net2"])
159
159
  """
160
160
 
161
161
  if not net_list:
@@ -208,8 +208,11 @@ class LayoutValidation:
208
208
  if isinstance(obj_dict[el], Primitive):
209
209
  if not obj_dict[el].is_void:
210
210
  sum += obj_dict[el].area()
211
- except:
212
- pass
211
+ except Exception as e:
212
+ self._pedb.logger.warning(
213
+ f"A(n) {type(e).__name__} error occurred while calculating area "
214
+ f"for element {elem} - Default value of 0 is used: {str(e)}"
215
+ )
213
216
  return sum
214
217
 
215
218
  if order_by_area:
@@ -336,3 +339,11 @@ class LayoutValidation:
336
339
  if fix:
337
340
  obj.aedt_name = f"via_{obj.id}"
338
341
  self._pedb.logger.info(f"Found {counts}/{len(pds)} padstacks have no name.")
342
+
343
+ @execution_timer("empty_pin_group")
344
+ def delete_empty_pin_groups(self):
345
+ for name, pg in self._pedb.siwave.pin_groups.items():
346
+ pins = pg.pins
347
+ if len(pins) == 0:
348
+ pg.delete()
349
+ self._pedb.logger.info(f"Pin group {name} deleted because it has no pins.")
@@ -243,9 +243,7 @@ class Material(object):
243
243
  if self.__dc_model and value:
244
244
  self.__dc_model.SetDCRelativePermitivity(value)
245
245
  else:
246
- self.__edb.logger.error(
247
- f"DC permittivity cannot be updated in material without DC model or value {value}." f""
248
- )
246
+ self.__edb.logger.error(f"DC permittivity cannot be updated in material without DC model or value {value}.")
249
247
 
250
248
  @property
251
249
  def dielectric_model_frequency(self):
@@ -23,6 +23,7 @@
23
23
  """
24
24
  This module contains these classes: `EdbLayout` and `Shape`.
25
25
  """
26
+
26
27
  import math
27
28
  import warnings
28
29
 
@@ -355,8 +356,11 @@ class Modeler(object):
355
356
  bounding_box.Item2.X.ToDouble(),
356
357
  bounding_box.Item2.Y.ToDouble(),
357
358
  ]
358
- except:
359
- pass
359
+ except Exception as e:
360
+ self._logger.warning(
361
+ f"A(n) {type(e).__name__} error occurred while retrieving bounding box for polygon {polygon} - "
362
+ f"Empty list is returned: {str(e)}"
363
+ )
360
364
  return bounding
361
365
 
362
366
  def get_polygon_points(self, polygon):
@@ -383,7 +387,7 @@ class Modeler(object):
383
387
  --------
384
388
 
385
389
  >>> poly = database.modeler.get_polygons_by_layer("GND")
386
- >>> points = database.modeler.get_polygon_points(poly[0])
390
+ >>> points = database.modeler.get_polygon_points(poly[0])
387
391
 
388
392
  """
389
393
  points = []
@@ -308,22 +308,30 @@ class EdbNets(CommonNets):
308
308
  val_value = cmp.rlc_values
309
309
  if refdes in exception_list:
310
310
  pass
311
- elif (
312
- val_type == "Inductor"
313
- and self._pedb.edb_value(val_value[1]).ToDouble() <= self._pedb.edb_value(inductor_below).ToDouble()
314
- ):
315
- pass
316
- elif (
317
- val_type == "Resistor"
318
- and self._pedb.edb_value(val_value[0]).ToDouble() <= self._pedb.edb_value(resistor_below).ToDouble()
319
- ):
320
- pass
321
- elif (
322
- val_type == "Capacitor"
323
- and self._pedb.edb_value(val_value[2]).ToDouble()
324
- >= self._pedb.edb_value(capacitor_above).ToDouble()
325
- ):
326
- pass
311
+ elif val_type == "Inductor":
312
+ if val_value[1] is None:
313
+ continue
314
+ elif (
315
+ not self._pedb.edb_value(val_value[1]).ToDouble()
316
+ <= self._pedb.edb_value(inductor_below).ToDouble()
317
+ ):
318
+ continue
319
+ elif val_type == "Resistor":
320
+ if val_value[0] is None:
321
+ continue
322
+ elif (
323
+ not self._pedb.edb_value(val_value[0]).ToDouble()
324
+ <= self._pedb.edb_value(resistor_below).ToDouble()
325
+ ):
326
+ continue
327
+ elif val_type == "Capacitor":
328
+ if val_value[2] is None:
329
+ continue
330
+ elif (
331
+ not self._pedb.edb_value(val_value[2]).ToDouble()
332
+ >= self._pedb.edb_value(capacitor_above).ToDouble()
333
+ ):
334
+ continue
327
335
  else:
328
336
  continue
329
337
 
@@ -575,7 +583,7 @@ class EdbNets(CommonNets):
575
583
  Examples
576
584
  --------
577
585
 
578
- >>> deleted_nets = database.nets.delete(["Net1","Net2"])
586
+ >>> deleted_nets = database.nets.delete(["Net1", "Net2"])
579
587
  """
580
588
  warnings.warn("Use :func:`delete` method instead.", DeprecationWarning)
581
589
  return self.delete(netlist=netlist)
@@ -596,7 +604,7 @@ class EdbNets(CommonNets):
596
604
  Examples
597
605
  --------
598
606
 
599
- >>> deleted_nets = database.nets.delete(["Net1","Net2"])
607
+ >>> deleted_nets = database.nets.delete(["Net1", "Net2"])
600
608
  """
601
609
  if isinstance(netlist, str):
602
610
  netlist = [netlist]
@@ -733,7 +741,7 @@ class EdbNets(CommonNets):
733
741
  Examples
734
742
  --------
735
743
 
736
- >>> renamed_nets = database.nets.find_and_fix_disjoint_nets(["GND","Net2"])
744
+ >>> renamed_nets = database.nets.find_and_fix_disjoint_nets(["GND", "Net2"])
737
745
  """
738
746
  warnings.warn("Use new function :func:`edb.layout_validation.disjoint_nets` instead.", DeprecationWarning)
739
747
  return self._pedb.layout_validation.disjoint_nets(
@@ -23,6 +23,7 @@
23
23
  """
24
24
  This module contains the `EdbPadstacks` class.
25
25
  """
26
+
26
27
  from collections import defaultdict
27
28
  import math
28
29
  from typing import Dict, List
@@ -708,8 +709,9 @@ class EdbPadstacks(object):
708
709
  )
709
710
  else: # pragma no cover
710
711
  self._logger.error(
711
- "Failed to reassign anti-pad value {} on Pads-stack definition {},"
712
- " layer{}".format(str(value), padstack.edb_padstack.GetName(), layer)
712
+ "Failed to reassign anti-pad value {} on Pads-stack definition {}, layer{}".format(
713
+ str(value), padstack.edb_padstack.GetName(), layer
714
+ )
713
715
  )
714
716
  all_succeed = False
715
717
  padstack.edb_padstack.SetData(cloned_padstack_data)
@@ -349,7 +349,7 @@ class AdvancedSettings(SettingsBase):
349
349
  ``True`` if automatic mesh is used, ``False`` otherwise.
350
350
  """
351
351
  warnings.warn(
352
- "`automatic_mesh` is deprecated." "Use `mesh_automatic` instead.",
352
+ "`automatic_mesh` is deprecated. Use `mesh_automatic` instead.",
353
353
  DeprecationWarning,
354
354
  )
355
355
  return self.sim_setup_info.simulation_settings.AdvancedSettings.MeshAutoMatic
@@ -713,6 +713,19 @@ class DCAdvancedSettings(SettingsBase):
713
713
  def max_num_pass(self):
714
714
  """Maximum number of passes.
715
715
 
716
+ deprecated: Use `max_num_passes` instead.
717
+
718
+ Returns
719
+ -------
720
+ int
721
+ """
722
+ warnings.warn("`max_num_pass` is deprecated. Use `max_num_passes` instead.", DeprecationWarning)
723
+ return self.max_num_passes
724
+
725
+ @property
726
+ def max_num_passes(self):
727
+ """Maximum number of passes.
728
+
716
729
  Returns
717
730
  -------
718
731
  int
@@ -723,6 +736,19 @@ class DCAdvancedSettings(SettingsBase):
723
736
  def min_num_pass(self):
724
737
  """Minimum number of passes.
725
738
 
739
+ deprecated: Use `min_num_passes` instead.
740
+
741
+ Returns
742
+ -------
743
+ int
744
+ """
745
+ warnings.warn("`min_num_pass` is deprecated. Use `min_num_passes` instead.", DeprecationWarning)
746
+ return self.sim_setup_info
747
+
748
+ @property
749
+ def min_num_passes(self):
750
+ """Minimum number of passes.
751
+
726
752
  Returns
727
753
  -------
728
754
  int
@@ -753,6 +779,19 @@ class DCAdvancedSettings(SettingsBase):
753
779
  def num_bondwire_sides(self):
754
780
  """Number of bondwire sides.
755
781
 
782
+ deprecated: Use `num_bw_sides` instead.
783
+
784
+ Returns
785
+ -------
786
+ int
787
+ """
788
+ warnings.warn("`num_bondwire_sides` is deprecated. Use `num_bw_sides` instead.", DeprecationWarning)
789
+ return self.num_bw_sides
790
+
791
+ @property
792
+ def num_bw_sides(self):
793
+ """Number of bondwire sides.
794
+
756
795
  Returns
757
796
  -------
758
797
  int
@@ -794,6 +833,20 @@ class DCAdvancedSettings(SettingsBase):
794
833
  def refine_bondwires(self):
795
834
  """Whether to refine mesh along bondwires.
796
835
 
836
+ deprecated: Use `refine_bws` instead.
837
+
838
+ Returns
839
+ -------
840
+ bool
841
+ ``True`` if refine bondwires is used, ``False`` otherwise.
842
+ """
843
+ warnings.warn("`refine_bondwires` is deprecated. Use `refine_bws` instead.", DeprecationWarning)
844
+ return self.refine_bws
845
+
846
+ @property
847
+ def refine_bws(self):
848
+ """Whether to refine mesh along bondwires.
849
+
797
850
  Returns
798
851
  -------
799
852
  bool
@@ -24,6 +24,7 @@
24
24
  This module contains these classes: ``CircuitPort``, ``CurrentSource``, ``EdbSiwave``,
25
25
  ``PinGroup``, ``ResistorSource``, ``Source``, ``SourceType``, and ``VoltageSource``.
26
26
  """
27
+
27
28
  import os
28
29
  import time
29
30
 
@@ -483,8 +484,8 @@ class EdbSiwave(object):
483
484
 
484
485
  >>> from pyedb import Edb
485
486
  >>> edbapp = Edb("myaedbfolder", "project name", "release version")
486
- >>> pins =edbapp.components.get_pin_from_component("U2A5")
487
- >>> edbapp.siwave.create_resistor_on_pin(pins[0], pins[1],50,"res_name")
487
+ >>> pins = edbapp.components.get_pin_from_component("U2A5")
488
+ >>> edbapp.siwave.create_resistor_on_pin(pins[0], pins[1], 50, "res_name")
488
489
  """
489
490
  resistor = ResistorSource()
490
491
  resistor.positive_node.net = pos_pin.net_name
@@ -625,7 +626,7 @@ class EdbSiwave(object):
625
626
 
626
627
  >>> from pyedb import Edb
627
628
  >>> edbapp = Edb("myaedbfolder", "project name", "release version")
628
- >>> edb.siwave.create_voltage_source_on_net("U2A5","V1P5_S3","U2A5","GND",3.3,0,"source_name")
629
+ >>> edb.siwave.create_voltage_source_on_net("U2A5", "V1P5_S3", "U2A5", "GND", 3.3, 0, "source_name")
629
630
  """
630
631
  if not negative_component_name:
631
632
  negative_component_name = positive_component_name
@@ -33,6 +33,11 @@ import logging
33
33
  import math
34
34
  import warnings
35
35
 
36
+ from defusedxml.ElementTree import parse as defused_parse
37
+ import matplotlib.colors as colors
38
+ import numpy as np
39
+ import pandas as pd
40
+
36
41
  from pyedb.dotnet.database.edb_data.layer_data import (
37
42
  LayerEdbClass,
38
43
  StackupLayerEdbClass,
@@ -42,24 +47,6 @@ from pyedb.dotnet.database.general import convert_py_list_to_net_list
42
47
  from pyedb.generic.general_methods import ET, generate_unique_name
43
48
  from pyedb.misc.aedtlib_personalib_install import write_pretty_xml
44
49
 
45
- colors = None
46
- pd = None
47
- np = None
48
- try:
49
- import matplotlib.colors as colors
50
- except ImportError:
51
- colors = None
52
-
53
- try:
54
- import numpy as np
55
- except ImportError:
56
- np = None
57
-
58
- try:
59
- import pandas as pd
60
- except ImportError:
61
- pd = None
62
-
63
50
  logger = logging.getLogger(__name__)
64
51
 
65
52
 
@@ -473,9 +460,6 @@ class Stackup(LayerCollection):
473
460
  -------
474
461
  bool
475
462
  """
476
- if not np:
477
- self._pedb.logger.error("Numpy is needed. Please, install it first.")
478
- return False
479
463
  if not layer_count % 2 == 0:
480
464
  return False
481
465
 
@@ -973,10 +957,6 @@ class Stackup(LayerCollection):
973
957
  return self.export(fpath, file_format=file_format, include_material_with_layer=include_material_with_layer)
974
958
 
975
959
  def _export_layer_stackup_to_csv_xlsx(self, fpath=None, file_format=None):
976
- if not pd:
977
- self._pedb.logger.error("Pandas is needed. Please, install it first.")
978
- return False
979
-
980
960
  data = {
981
961
  "Type": [],
982
962
  "Material": [],
@@ -1137,7 +1117,7 @@ class Stackup(LayerCollection):
1137
1117
 
1138
1118
  Examples
1139
1119
  --------
1140
- >>> edb = Edb(edbpath=targetfile, edbversion="2021.2")
1120
+ >>> edb = Edb(edbpath=targetfile, edbversion="2021.2")
1141
1121
  >>> edb.stackup.flip_design()
1142
1122
  >>> edb.save()
1143
1123
  >>> edb.close_edb()
@@ -1227,8 +1207,11 @@ class Stackup(LayerCollection):
1227
1207
  sball_prop = cmp_prop.GetSolderBallProperty().Clone()
1228
1208
  sball_prop.SetPlacement(self._pedb.definition.SolderballPlacement.AbovePadstack)
1229
1209
  cmp_prop.SetSolderBallProperty(sball_prop)
1230
- except:
1231
- pass
1210
+ except Exception as e:
1211
+ self._logger.warning(
1212
+ f"A(n) {type(e).__name__} error occurred while attempting to update "
1213
+ f"SolderBallProperty for component {cmp}: {str(e)}"
1214
+ )
1232
1215
  if cmp_type == self._pedb.definition.ComponentType.IC:
1233
1216
  die_prop = cmp_prop.GetDieProperty().Clone()
1234
1217
  chip_orientation = die_prop.GetOrientation()
@@ -1356,22 +1339,28 @@ class Stackup(LayerCollection):
1356
1339
 
1357
1340
  Examples
1358
1341
  --------
1359
- >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1342
+ >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1360
1343
  >>> edb2 = Edb(edbpath=targetfile2, edbversion="2021.2")
1361
1344
 
1362
1345
  >>> hosting_cmp = edb1.components.get_component_by_name("U100")
1363
1346
  >>> mounted_cmp = edb2.components.get_component_by_name("BGA")
1364
1347
 
1365
1348
  >>> vector, rotation, solder_ball_height = edb1.components.get_component_placement_vector(
1366
- ... mounted_component=mounted_cmp,
1367
- ... hosting_component=hosting_cmp,
1368
- ... mounted_component_pin1="A12",
1369
- ... mounted_component_pin2="A14",
1370
- ... hosting_component_pin1="A12",
1371
- ... hosting_component_pin2="A14")
1372
- >>> edb2.stackup.place_in_layout(edb1.active_cell, angle=0.0, offset_x=vector[0],
1373
- ... offset_y=vector[1], flipped_stackup=False, place_on_top=True,
1374
- ... )
1349
+ ... mounted_component=mounted_cmp,
1350
+ ... hosting_component=hosting_cmp,
1351
+ ... mounted_component_pin1="A12",
1352
+ ... mounted_component_pin2="A14",
1353
+ ... hosting_component_pin1="A12",
1354
+ ... hosting_component_pin2="A14",
1355
+ ... )
1356
+ >>> edb2.stackup.place_in_layout(
1357
+ ... edb1.active_cell,
1358
+ ... angle=0.0,
1359
+ ... offset_x=vector[0],
1360
+ ... offset_y=vector[1],
1361
+ ... flipped_stackup=False,
1362
+ ... place_on_top=True,
1363
+ ... )
1375
1364
  """
1376
1365
  # if flipped_stackup and place_on_top or (not flipped_stackup and not place_on_top):
1377
1366
  self.adjust_solder_dielectrics()
@@ -1453,13 +1442,18 @@ class Stackup(LayerCollection):
1453
1442
 
1454
1443
  Examples
1455
1444
  --------
1456
- >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1445
+ >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1457
1446
  >>> edb2 = Edb(edbpath=targetfile2, edbversion="2021.2")
1458
1447
  >>> hosting_cmp = edb1.components.get_component_by_name("U100")
1459
1448
  >>> mounted_cmp = edb2.components.get_component_by_name("BGA")
1460
- >>> edb2.stackup.place_in_layout(edb1.active_cell, angle=0.0, offset_x="1mm",
1461
- ... offset_y="2mm", flipped_stackup=False, place_on_top=True,
1462
- ... )
1449
+ >>> edb2.stackup.place_in_layout(
1450
+ ... edb1.active_cell,
1451
+ ... angle=0.0,
1452
+ ... offset_x="1mm",
1453
+ ... offset_y="2mm",
1454
+ ... flipped_stackup=False,
1455
+ ... place_on_top=True,
1456
+ ... )
1463
1457
  """
1464
1458
  _angle = angle * math.pi / 180.0
1465
1459
 
@@ -1592,13 +1586,18 @@ class Stackup(LayerCollection):
1592
1586
 
1593
1587
  Examples
1594
1588
  --------
1595
- >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1589
+ >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1596
1590
  >>> edb2 = Edb(edbpath=targetfile2, edbversion="2021.2")
1597
1591
  >>> hosting_cmp = edb1.components.get_component_by_name("U100")
1598
1592
  >>> mounted_cmp = edb2.components.get_component_by_name("BGA")
1599
- >>> edb1.stackup.place_instance(edb2, angle=0.0, offset_x="1mm",
1600
- ... offset_y="2mm", flipped_stackup=False, place_on_top=True,
1601
- ... )
1593
+ >>> edb1.stackup.place_instance(
1594
+ ... edb2,
1595
+ ... angle=0.0,
1596
+ ... offset_x="1mm",
1597
+ ... offset_y="2mm",
1598
+ ... flipped_stackup=False,
1599
+ ... place_on_top=True,
1600
+ ... )
1602
1601
  """
1603
1602
  _angle = angle * math.pi / 180.0
1604
1603
 
@@ -1736,11 +1735,16 @@ class Stackup(LayerCollection):
1736
1735
 
1737
1736
  Examples
1738
1737
  --------
1739
- >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1738
+ >>> edb1 = Edb(edbpath=targetfile1, edbversion="2021.2")
1740
1739
  >>> a3dcomp_path = "connector.a3dcomp"
1741
- >>> edb1.stackup.place_a3dcomp_3d_placement(a3dcomp_path, angle=0.0, offset_x="1mm",
1742
- ... offset_y="2mm", flipped_stackup=False, place_on_top=True,
1743
- ... )
1740
+ >>> edb1.stackup.place_a3dcomp_3d_placement(
1741
+ ... a3dcomp_path,
1742
+ ... angle=0.0,
1743
+ ... offset_x="1mm",
1744
+ ... offset_y="2mm",
1745
+ ... flipped_stackup=False,
1746
+ ... place_on_top=True,
1747
+ ... )
1744
1748
  """
1745
1749
  zero_data = self._edb_value(0.0)
1746
1750
  one_data = self._edb_value(1.0)
@@ -1795,7 +1799,7 @@ class Stackup(LayerCollection):
1795
1799
 
1796
1800
  Examples
1797
1801
  --------
1798
- >>> edb = Edb(edbpath=targetfile1, edbversion="2021.2")
1802
+ >>> edb = Edb(edbpath=targetfile1, edbversion="2021.2")
1799
1803
  >>> edb.stackup.residual_copper_area_per_layer()
1800
1804
  """
1801
1805
  temp_data = {name: 0 for name, _ in self.signal_layers.items()}
@@ -1972,10 +1976,6 @@ class Stackup(LayerCollection):
1972
1976
  file_path : str
1973
1977
  File path to the CSV file.
1974
1978
  """
1975
- if not pd:
1976
- self._pedb.logger.error("Pandas is needed. You must install it first.")
1977
- return False
1978
-
1979
1979
  df = pd.read_csv(file_path, index_col=0)
1980
1980
 
1981
1981
  for name in self.layers.keys(): # pragma: no cover
@@ -2242,9 +2242,6 @@ class Stackup(LayerCollection):
2242
2242
  ``True`` when successful, ``False`` when failed.
2243
2243
  """
2244
2244
 
2245
- if not colors:
2246
- self._pedb.logger.error("Matplotlib is needed. Please, install it first.")
2247
- return False
2248
2245
  tree = ET.parse(file_path)
2249
2246
  root = tree.getroot()
2250
2247
  stackup = root.find("Stackup")
@@ -1,5 +1,4 @@
1
1
  class HeatSink:
2
-
3
2
  """Heatsink model description.
4
3
 
5
4
  Parameters
@@ -382,6 +382,87 @@ class HfssSimulationSetup(SimulationSetup):
382
382
  return False
383
383
  return True
384
384
 
385
+ def auto_mesh_operation(
386
+ self,
387
+ trace_ratio_seeding: float = 3,
388
+ signal_via_side_number: int = 12,
389
+ power_ground_via_side_number: int = 6,
390
+ ) -> bool:
391
+ """
392
+ Automatically create and apply a length-based mesh operation for all nets in the design.
393
+
394
+ The method inspects every signal net, determines the smallest trace width, and
395
+ seeds a :class:`LengthMeshOperation` whose maximum element length is
396
+ ``smallest_width * trace_ratio_seeding``. Signal vias (padstack instances) are
397
+ configured with the requested number of polygon sides, while power/ground vias
398
+ are updated through the global ``num_via_sides`` advanced setting.
399
+
400
+ Parameters
401
+ ----------
402
+ trace_ratio_seeding : float, optional
403
+ Ratio used to compute the maximum allowed element length from the
404
+ smallest trace width found in the design. The resulting length is
405
+ ``min_width * trace_ratio_seeding``. Defaults to ``3``.
406
+ signal_via_side_number : int, optional
407
+ Number of sides (i.e. faceting resolution) assigned to **signal**
408
+ padstack instances that belong to the nets being meshed.
409
+ Defaults to ``12``.
410
+ power_ground_via_side_number : int, optional
411
+ Number of sides assigned to **power/ground** vias via the global
412
+ ``advanced.num_via_sides`` setting. Defaults to ``6``.
413
+
414
+ Returns
415
+ -------
416
+ bool
417
+
418
+ Raises
419
+ ------
420
+ ValueError
421
+ If the design contains no terminals, making mesh seeding impossible.
422
+
423
+ Notes
424
+ -----
425
+ * Only primitives of type ``"path"`` are considered when determining the
426
+ smallest trace width.
427
+ * Every ``(net, layer, sheet)`` tuple required by the mesher is
428
+ automatically populated; sheet are explicitly marked as ``False``.
429
+ * Existing contents of :attr:`mesh_operations` are **replaced** by the
430
+ single new operation.
431
+
432
+ Examples
433
+ --------
434
+ >>> setup = edbapp.setups["my_setup"]
435
+ >>> setup.auto_mesh_operation(trace_ratio_seeding=4, signal_vias_side_number=16)
436
+ >>> setup.mesh_operations[0].max_length
437
+ '2.5um'
438
+ """
439
+ net_for_mesh_seeding = list(set([term.net.name for term in list(self._pedb.terminals.values())]))
440
+ if not net_for_mesh_seeding:
441
+ raise ValueError("No terminals found to seed the mesh operation.")
442
+ net_layer_dict = {}
443
+ smallest_width = 1e3
444
+ for net in net_for_mesh_seeding:
445
+ net_layer_dict[net] = []
446
+ traces = [prim for prim in self._pedb.modeler.primitives_by_net[net] if prim.type.lower() == "path"]
447
+ _width = min([trace.width for trace in traces], default=1e3)
448
+ if _width < smallest_width:
449
+ smallest_width = _width
450
+ layers = list(set([trace.layer.name for trace in traces]))
451
+ for layer in layers:
452
+ net_layer_dict[net].append(layer)
453
+ for inst in [
454
+ inst for inst in list(self._pedb.padstacks.padstack_instances.values()) if inst.net_name == net
455
+ ]:
456
+ inst.side_number = signal_via_side_number
457
+ self.add_length_mesh_operation(
458
+ net_layer_list=net_layer_dict,
459
+ name=f"{self.name}_AutoMeshOp",
460
+ max_length=f"{round(float((smallest_width * trace_ratio_seeding)), 9) * 1e6}um",
461
+ )
462
+ if f"{self.name}_AutoMeshOp" in self.mesh_operations:
463
+ return True
464
+ return False
465
+
385
466
 
386
467
  class HFSSPISimulationSetup(SimulationSetup):
387
468
  """Manages EDB methods for HFSSPI simulation setup."""
@@ -371,11 +371,13 @@ class SimulationSetup(object):
371
371
  Examples
372
372
  --------
373
373
  >>> setup1 = edbapp.create_siwave_syz_setup("setup1")
374
- >>> setup1.add_frequency_sweep(frequency_sweep=[
375
- ... ["linear count", "0", "1kHz", 1],
376
- ... ["log scale", "1kHz", "0.1GHz", 10],
377
- ... ["linear scale", "0.1GHz", "10GHz", "0.1GHz"],
378
- ... ])
374
+ >>> setup1.add_frequency_sweep(
375
+ ... frequency_sweep=[
376
+ ... ["linear count", "0", "1kHz", 1],
377
+ ... ["log scale", "1kHz", "0.1GHz", 10],
378
+ ... ["linear scale", "0.1GHz", "10GHz", "0.1GHz"],
379
+ ... ]
380
+ ... )
379
381
  """
380
382
  warnings.warn("`add_frequency_sweep` is deprecated. Use `add_sweep` method instead.", DeprecationWarning)
381
383
  return self.add_sweep(name, frequency_sweep)
@@ -691,6 +691,7 @@ class SIWaveCPASimulationSetup:
691
691
  self._pedb = pedb
692
692
  self._channel_setup = ChannelSetup(pedb)
693
693
  self._solver_options = SolverOptions(pedb)
694
+ self.type = "cpa"
694
695
  if isinstance(siwave_cpa_setup_class, SIwaveCpaSetup):
695
696
  self._apply_cfg_object(siwave_cpa_setup_class)
696
697
  else: