pyedb 0.19.0__py3-none-any.whl → 0.21.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/dotnet/edb.py +311 -335
  3. pyedb/dotnet/edb_core/cell/connectable.py +64 -0
  4. pyedb/dotnet/edb_core/cell/hierarchy/component.py +12 -10
  5. pyedb/dotnet/edb_core/cell/hierarchy/hierarchy_obj.py +1 -1
  6. pyedb/dotnet/edb_core/cell/layout.py +253 -105
  7. pyedb/dotnet/edb_core/cell/layout_obj.py +4 -49
  8. pyedb/dotnet/edb_core/cell/primitive.py +14 -2
  9. pyedb/dotnet/edb_core/cell/terminal/padstack_instance_terminal.py +1 -1
  10. pyedb/dotnet/edb_core/cell/terminal/point_terminal.py +1 -1
  11. pyedb/dotnet/edb_core/cell/terminal/terminal.py +1 -19
  12. pyedb/dotnet/edb_core/cell/voltage_regulator.py +2 -16
  13. pyedb/dotnet/edb_core/components.py +14 -13
  14. pyedb/dotnet/edb_core/dotnet/database.py +5 -10
  15. pyedb/dotnet/edb_core/dotnet/primitive.py +11 -1
  16. pyedb/dotnet/edb_core/edb_data/control_file.py +2 -12
  17. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +20 -33
  18. pyedb/dotnet/edb_core/general.py +6 -9
  19. pyedb/dotnet/edb_core/hfss.py +4 -8
  20. pyedb/dotnet/edb_core/layout_obj_instance.py +30 -0
  21. pyedb/dotnet/edb_core/materials.py +4 -11
  22. pyedb/dotnet/edb_core/{layout.py → modeler.py} +153 -7
  23. pyedb/dotnet/edb_core/net_class.py +7 -8
  24. pyedb/dotnet/edb_core/nets.py +3 -9
  25. pyedb/dotnet/edb_core/padstack.py +13 -9
  26. pyedb/dotnet/edb_core/sim_setup_data/data/sweep_data.py +5 -2
  27. pyedb/dotnet/edb_core/siwave.py +5 -6
  28. pyedb/dotnet/edb_core/stackup.py +18 -23
  29. pyedb/dotnet/edb_core/utilities/simulation_setup.py +1 -4
  30. pyedb/generic/filesystem.py +2 -8
  31. pyedb/generic/general_methods.py +4 -10
  32. pyedb/generic/plot.py +26 -29
  33. pyedb/generic/process.py +2 -6
  34. pyedb/misc/downloads.py +3 -40
  35. pyedb/siwave.py +2 -5
  36. {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/METADATA +2 -2
  37. {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/RECORD +39 -38
  38. pyedb/dotnet/edb_core/dotnet/layout.py +0 -260
  39. {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/LICENSE +0 -0
  40. {pyedb-0.19.0.dist-info → pyedb-0.21.0.dist-info}/WHEEL +0 -0
pyedb/dotnet/edb.py CHANGED
@@ -30,6 +30,7 @@ import os
30
30
  from pathlib import Path
31
31
  import re
32
32
  import shutil
33
+ import subprocess
33
34
  import sys
34
35
  import tempfile
35
36
  import time
@@ -37,21 +38,14 @@ import traceback
37
38
  from typing import Union
38
39
  import warnings
39
40
 
41
+ import rtree
42
+
40
43
  from pyedb.configuration.configuration import Configuration
41
44
  from pyedb.dotnet.application.Variables import decompose_variable_value
42
45
  from pyedb.dotnet.edb_core.cell.layout import Layout
43
- from pyedb.dotnet.edb_core.cell.terminal.bundle_terminal import BundleTerminal
44
- from pyedb.dotnet.edb_core.cell.terminal.edge_terminal import EdgeTerminal
45
- from pyedb.dotnet.edb_core.cell.terminal.padstack_instance_terminal import (
46
- PadstackInstanceTerminal,
47
- )
48
- from pyedb.dotnet.edb_core.cell.terminal.pingroup_terminal import PinGroupTerminal
49
- from pyedb.dotnet.edb_core.cell.terminal.point_terminal import PointTerminal
50
46
  from pyedb.dotnet.edb_core.cell.terminal.terminal import Terminal
51
- from pyedb.dotnet.edb_core.cell.voltage_regulator import VoltageRegulator
52
47
  from pyedb.dotnet.edb_core.components import Components
53
48
  from pyedb.dotnet.edb_core.dotnet.database import Database
54
- from pyedb.dotnet.edb_core.dotnet.layout import LayoutDotNet
55
49
  from pyedb.dotnet.edb_core.edb_data.control_file import (
56
50
  ControlFile,
57
51
  convert_technology_file,
@@ -82,6 +76,7 @@ from pyedb.dotnet.edb_core.general import (
82
76
  from pyedb.dotnet.edb_core.hfss import EdbHfss
83
77
  from pyedb.dotnet.edb_core.layout_validation import LayoutValidation
84
78
  from pyedb.dotnet.edb_core.materials import Materials
79
+ from pyedb.dotnet.edb_core.modeler import Modeler
85
80
  from pyedb.dotnet.edb_core.net_class import (
86
81
  EdbDifferentialPairs,
87
82
  EdbExtendedNets,
@@ -103,8 +98,6 @@ from pyedb.generic.constants import AEDT_UNITS, SolverType
103
98
  from pyedb.generic.general_methods import (
104
99
  generate_unique_name,
105
100
  get_string_version,
106
- inside_desktop,
107
- is_ironpython,
108
101
  is_linux,
109
102
  is_windows,
110
103
  )
@@ -114,13 +107,6 @@ from pyedb.ipc2581.ipc2581 import Ipc2581
114
107
  from pyedb.modeler.geometry_operators import GeometryOperators
115
108
  from pyedb.workflow import Workflow
116
109
 
117
- if is_linux and is_ironpython:
118
- import subprocessdotnet as subprocess
119
- else:
120
- import subprocess
121
-
122
- import rtree
123
-
124
110
 
125
111
  class Edb(Database):
126
112
  """Provides the EDB application interface.
@@ -235,9 +221,7 @@ class Edb(Database):
235
221
  if not isreadonly:
236
222
  self._check_remove_project_files(edbpath, remove_existing_aedt)
237
223
 
238
- if isaedtowned and (inside_desktop or settings.remote_rpc_session):
239
- self.open_edb_inside_aedt()
240
- elif edbpath[-3:] in ["brd", "mcm", "sip", "gds", "xml", "dxf", "tgz", "anf"]:
224
+ if edbpath[-3:] in ["brd", "mcm", "sip", "gds", "xml", "dxf", "tgz", "anf"]:
241
225
  self.edbpath = edbpath[:-4] + ".aedb"
242
226
  working_dir = os.path.dirname(edbpath)
243
227
  control_file = None
@@ -361,7 +345,7 @@ class Edb(Database):
361
345
  self._siwave = EdbSiwave(self)
362
346
  self._hfss = EdbHfss(self)
363
347
  self._nets = EdbNets(self)
364
- self._core_primitives = Layout(self, self._active_cell.GetLayout())
348
+ self._core_primitives = Modeler(self)
365
349
  self._stackup2 = self._stackup
366
350
  self._materials = Materials(self)
367
351
 
@@ -437,23 +421,10 @@ class Edb(Database):
437
421
 
438
422
  Returns
439
423
  -------
440
- Terminal dictionary : Dict[str, pyedb.dotnet.edb_core.edb_data.terminals.Terminal]
424
+ Dict
441
425
  """
442
426
 
443
- temp = {}
444
- for i in self.layout.terminals:
445
- terminal_type = i.ToString().split(".")[-1]
446
- if terminal_type == "PinGroupTerminal":
447
- temp[i.GetName()] = PinGroupTerminal(self, i)
448
- elif terminal_type == "PadstackInstanceTerminal":
449
- temp[i.GetName()] = PadstackInstanceTerminal(self, i)
450
- elif terminal_type == "EdgeTerminal":
451
- temp[i.GetName()] = EdgeTerminal(self, i)
452
- elif terminal_type == "BundleTerminal":
453
- temp[i.GetName()] = BundleTerminal(self, i)
454
- elif terminal_type == "PointTerminal":
455
- temp[i.GetName()] = PointTerminal(self, i)
456
- return temp
427
+ return {i.name: i for i in self.layout.terminals}
457
428
 
458
429
  @property
459
430
  def excitations(self):
@@ -516,7 +487,7 @@ class Edb(Database):
516
487
  @property
517
488
  def voltage_regulator_modules(self):
518
489
  """Get all voltage regulator modules"""
519
- vrms = [VoltageRegulator(self, edb_object) for edb_object in list(self.active_layout.VoltageRegulators)]
490
+ vrms = self.layout.voltage_regulators
520
491
  _vrms = {}
521
492
  for vrm in vrms:
522
493
  _vrms[vrm.name] = vrm
@@ -574,40 +545,6 @@ class Edb(Database):
574
545
 
575
546
  return True
576
547
 
577
- def open_edb_inside_aedt(self):
578
- """Open EDB inside AEDT.
579
-
580
- Returns
581
- -------
582
- ``True`` when succeed ``False`` if failed : bool
583
-
584
- """
585
- self.logger.info("Opening EDB from HDL")
586
- self.run_as_standalone(False)
587
- if self.oproject.GetEDBHandle():
588
- self.attach(self.oproject.GetEDBHandle())
589
- if not self.active_db:
590
- self.logger.warning("Error getting the database.")
591
- self._active_cell = None
592
- return None
593
- self._active_cell = self.edb_api.cell.cell.FindByName(
594
- self.active_db,
595
- self.edb_api.cell._cell.CellType.CircuitCell,
596
- self.cellname,
597
- )
598
- if self._active_cell is None:
599
- self._active_cell = list(self.top_circuit_cells)[0]
600
- if self._active_cell:
601
- if not os.path.exists(self.edbpath):
602
- os.makedirs(self.edbpath)
603
- self._init_objects()
604
- return True
605
- else:
606
- return None
607
- else:
608
- self._active_cell = None
609
- return None
610
-
611
548
  def create_edb(self):
612
549
  """Create EDB.
613
550
 
@@ -1153,7 +1090,7 @@ class Edb(Database):
1153
1090
  >>> top_prims = edbapp.modeler.primitives_by_layer["TOP"]
1154
1091
  """
1155
1092
  if not self._core_primitives and self.active_db:
1156
- self._core_primitives = Layout(self, self._active_cell.GetLayout())
1093
+ self._core_primitives = Modeler(self)
1157
1094
  return self._core_primitives
1158
1095
 
1159
1096
  @property
@@ -1164,7 +1101,7 @@ class Edb(Database):
1164
1101
  -------
1165
1102
  :class:`legacy.edb_core.dotnet.layout.Layout`
1166
1103
  """
1167
- return LayoutDotNet(self)
1104
+ return Layout(self, self._active_cell.GetLayout())
1168
1105
 
1169
1106
  @property
1170
1107
  def active_layout(self):
@@ -1174,12 +1111,12 @@ class Edb(Database):
1174
1111
  -------
1175
1112
  Instance of EDB API Layout Class.
1176
1113
  """
1177
- return self.layout._layout
1114
+ return self.layout._edb_object
1178
1115
 
1179
1116
  @property
1180
1117
  def layout_instance(self):
1181
1118
  """Edb Layout Instance."""
1182
- return self.layout.layout_instance
1119
+ return self.layout._edb_object.GetLayoutInstance()
1183
1120
 
1184
1121
  def get_connected_objects(self, layout_object_instance):
1185
1122
  """Get connected objects.
@@ -2218,9 +2155,6 @@ class Edb(Database):
2218
2155
  keep_lines_as_path=False,
2219
2156
  inlcude_voids_in_extents=False,
2220
2157
  ):
2221
- if is_ironpython: # pragma: no cover
2222
- self.logger.error("Method working only in Cpython")
2223
- return False
2224
2158
  from concurrent.futures import ThreadPoolExecutor
2225
2159
 
2226
2160
  if output_aedb_path:
@@ -2750,20 +2684,12 @@ class Edb(Database):
2750
2684
 
2751
2685
  for void_circle in voids_to_add:
2752
2686
  if void_circle.type == "Circle":
2753
- if is_ironpython: # pragma: no cover
2754
- (
2755
- res,
2756
- center_x,
2757
- center_y,
2758
- radius,
2759
- ) = void_circle.primitive_object.GetParameters()
2760
- else:
2761
- (
2762
- res,
2763
- center_x,
2764
- center_y,
2765
- radius,
2766
- ) = void_circle.primitive_object.GetParameters(0.0, 0.0, 0.0)
2687
+ (
2688
+ res,
2689
+ center_x,
2690
+ center_y,
2691
+ radius,
2692
+ ) = void_circle.primitive_object.GetParameters(0.0, 0.0, 0.0)
2767
2693
  cloned_circle = self.edb_api.cell.primitive.circle.create(
2768
2694
  layout,
2769
2695
  void_circle.layer_name,
@@ -3344,163 +3270,156 @@ class Edb(Database):
3344
3270
  legacy_name = self.edbpath
3345
3271
  if simulation_setup.output_aedb:
3346
3272
  self.save_edb_as(simulation_setup.output_aedb)
3347
- try:
3348
- if simulation_setup.signal_layer_etching_instances:
3349
- for layer in simulation_setup.signal_layer_etching_instances:
3350
- if layer in self.stackup.layers:
3351
- idx = simulation_setup.signal_layer_etching_instances.index(layer)
3352
- if len(simulation_setup.etching_factor_instances) > idx:
3353
- self.stackup[layer].etch_factor = float(simulation_setup.etching_factor_instances[idx])
3354
-
3355
- if not simulation_setup.signal_nets and simulation_setup.components:
3356
- nets_to_include = []
3357
- pnets = list(self.nets.power.keys())[:]
3358
- for el in simulation_setup.components:
3359
- nets_to_include.append([i for i in self.components[el].nets if i not in pnets])
3360
- simulation_setup.signal_nets = [
3361
- i
3362
- for i in list(set.intersection(*map(set, nets_to_include)))
3363
- if i not in simulation_setup.power_nets and i != ""
3364
- ]
3365
- self.nets.classify_nets(simulation_setup.power_nets, simulation_setup.signal_nets)
3366
- if not simulation_setup.power_nets or not simulation_setup.signal_nets:
3367
- self.logger.info("Disabling cutout as no signals or power nets have been defined.")
3368
- simulation_setup.do_cutout_subdesign = False
3369
- if simulation_setup.do_cutout_subdesign:
3370
- self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
3371
- if simulation_setup.use_default_cutout:
3372
- old_cell_name = self.active_cell.GetName()
3373
- if self.cutout(
3374
- signal_list=simulation_setup.signal_nets,
3375
- reference_list=simulation_setup.power_nets,
3376
- expansion_size=simulation_setup.cutout_subdesign_expansion,
3377
- use_round_corner=simulation_setup.cutout_subdesign_round_corner,
3378
- extent_type=simulation_setup.cutout_subdesign_type,
3379
- use_pyaedt_cutout=False,
3380
- use_pyaedt_extent_computing=False,
3381
- ):
3382
- self.logger.info("Cutout processed.")
3383
- old_cell = self.active_cell.FindByName(
3384
- self.db,
3385
- self.edb_api.cell.CellType.CircuitCell,
3386
- old_cell_name,
3387
- )
3388
- if old_cell:
3389
- old_cell.Delete()
3390
- else: # pragma: no cover
3391
- self.logger.error("Cutout failed.")
3392
- else:
3393
- self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
3394
- self.cutout(
3395
- signal_list=simulation_setup.signal_nets,
3396
- reference_list=simulation_setup.power_nets,
3397
- expansion_size=simulation_setup.cutout_subdesign_expansion,
3398
- use_round_corner=simulation_setup.cutout_subdesign_round_corner,
3399
- extent_type=simulation_setup.cutout_subdesign_type,
3400
- use_pyaedt_cutout=True,
3401
- use_pyaedt_extent_computing=True,
3402
- remove_single_pin_components=True,
3403
- )
3273
+ if simulation_setup.signal_layer_etching_instances:
3274
+ for layer in simulation_setup.signal_layer_etching_instances:
3275
+ if layer in self.stackup.layers:
3276
+ idx = simulation_setup.signal_layer_etching_instances.index(layer)
3277
+ if len(simulation_setup.etching_factor_instances) > idx:
3278
+ self.stackup[layer].etch_factor = float(simulation_setup.etching_factor_instances[idx])
3279
+
3280
+ if not simulation_setup.signal_nets and simulation_setup.components:
3281
+ nets_to_include = []
3282
+ pnets = list(self.nets.power.keys())[:]
3283
+ for el in simulation_setup.components:
3284
+ nets_to_include.append([i for i in self.components[el].nets if i not in pnets])
3285
+ simulation_setup.signal_nets = [
3286
+ i
3287
+ for i in list(set.intersection(*map(set, nets_to_include)))
3288
+ if i not in simulation_setup.power_nets and i != ""
3289
+ ]
3290
+ self.nets.classify_nets(simulation_setup.power_nets, simulation_setup.signal_nets)
3291
+ if not simulation_setup.power_nets or not simulation_setup.signal_nets:
3292
+ self.logger.info("Disabling cutout as no signals or power nets have been defined.")
3293
+ simulation_setup.do_cutout_subdesign = False
3294
+ if simulation_setup.do_cutout_subdesign:
3295
+ self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
3296
+ if simulation_setup.use_default_cutout:
3297
+ old_cell_name = self.active_cell.GetName()
3298
+ if self.cutout(
3299
+ signal_list=simulation_setup.signal_nets,
3300
+ reference_list=simulation_setup.power_nets,
3301
+ expansion_size=simulation_setup.cutout_subdesign_expansion,
3302
+ use_round_corner=simulation_setup.cutout_subdesign_round_corner,
3303
+ extent_type=simulation_setup.cutout_subdesign_type,
3304
+ use_pyaedt_cutout=False,
3305
+ use_pyaedt_extent_computing=False,
3306
+ ):
3404
3307
  self.logger.info("Cutout processed.")
3308
+ old_cell = self.active_cell.FindByName(
3309
+ self.db,
3310
+ self.edb_api.cell.CellType.CircuitCell,
3311
+ old_cell_name,
3312
+ )
3313
+ if old_cell:
3314
+ old_cell.Delete()
3315
+ else: # pragma: no cover
3316
+ self.logger.error("Cutout failed.")
3405
3317
  else:
3406
- if simulation_setup.include_only_selected_nets:
3407
- included_nets = simulation_setup.signal_nets + simulation_setup.power_nets
3408
- nets_to_remove = [
3409
- net.name for net in list(self.nets.nets.values()) if not net.name in included_nets
3410
- ]
3411
- self.nets.delete(nets_to_remove)
3412
- self.logger.info("Deleting existing ports.")
3413
- map(lambda port: port.Delete(), self.layout.terminals)
3414
- map(lambda pg: pg.Delete(), self.layout.pin_groups)
3415
- if simulation_setup.solver_type == SolverType.Hfss3dLayout:
3416
- if simulation_setup.generate_excitations:
3417
- self.logger.info("Creating HFSS ports for signal nets.")
3418
- source_type = SourceType.CoaxPort
3419
- if not simulation_setup.generate_solder_balls:
3420
- source_type = SourceType.CircPort
3421
- for cmp in simulation_setup.components:
3422
- if isinstance(cmp, str): # keep legacy component
3318
+ self.logger.info("Cutting out using method: {0}".format(simulation_setup.cutout_subdesign_type))
3319
+ self.cutout(
3320
+ signal_list=simulation_setup.signal_nets,
3321
+ reference_list=simulation_setup.power_nets,
3322
+ expansion_size=simulation_setup.cutout_subdesign_expansion,
3323
+ use_round_corner=simulation_setup.cutout_subdesign_round_corner,
3324
+ extent_type=simulation_setup.cutout_subdesign_type,
3325
+ use_pyaedt_cutout=True,
3326
+ use_pyaedt_extent_computing=True,
3327
+ remove_single_pin_components=True,
3328
+ )
3329
+ self.logger.info("Cutout processed.")
3330
+ else:
3331
+ if simulation_setup.include_only_selected_nets:
3332
+ included_nets = simulation_setup.signal_nets + simulation_setup.power_nets
3333
+ nets_to_remove = [net.name for net in list(self.nets.nets.values()) if not net.name in included_nets]
3334
+ self.nets.delete(nets_to_remove)
3335
+ self.logger.info("Deleting existing ports.")
3336
+ map(lambda port: port.Delete(), self.layout.terminals)
3337
+ map(lambda pg: pg.Delete(), self.layout.pin_groups)
3338
+ if simulation_setup.solver_type == SolverType.Hfss3dLayout:
3339
+ if simulation_setup.generate_excitations:
3340
+ self.logger.info("Creating HFSS ports for signal nets.")
3341
+ source_type = SourceType.CoaxPort
3342
+ if not simulation_setup.generate_solder_balls:
3343
+ source_type = SourceType.CircPort
3344
+ for cmp in simulation_setup.components:
3345
+ if isinstance(cmp, str): # keep legacy component
3346
+ self.components.create_port_on_component(
3347
+ cmp,
3348
+ net_list=simulation_setup.signal_nets,
3349
+ do_pingroup=False,
3350
+ reference_net=simulation_setup.power_nets,
3351
+ port_type=source_type,
3352
+ )
3353
+ elif isinstance(cmp, dict):
3354
+ if "refdes" in cmp:
3355
+ if not "solder_balls_height" in cmp: # pragma no cover
3356
+ cmp["solder_balls_height"] = None
3357
+ if not "solder_balls_size" in cmp: # pragma no cover
3358
+ cmp["solder_balls_size"] = None
3359
+ cmp["solder_balls_mid_size"] = None
3360
+ if not "solder_balls_mid_size" in cmp: # pragma no cover
3361
+ cmp["solder_balls_mid_size"] = None
3423
3362
  self.components.create_port_on_component(
3424
- cmp,
3363
+ cmp["refdes"],
3425
3364
  net_list=simulation_setup.signal_nets,
3426
3365
  do_pingroup=False,
3427
3366
  reference_net=simulation_setup.power_nets,
3428
3367
  port_type=source_type,
3368
+ solder_balls_height=cmp["solder_balls_height"],
3369
+ solder_balls_size=cmp["solder_balls_size"],
3370
+ solder_balls_mid_size=cmp["solder_balls_mid_size"],
3429
3371
  )
3430
- elif isinstance(cmp, dict):
3431
- if "refdes" in cmp:
3432
- if not "solder_balls_height" in cmp: # pragma no cover
3433
- cmp["solder_balls_height"] = None
3434
- if not "solder_balls_size" in cmp: # pragma no cover
3435
- cmp["solder_balls_size"] = None
3436
- cmp["solder_balls_mid_size"] = None
3437
- if not "solder_balls_mid_size" in cmp: # pragma no cover
3438
- cmp["solder_balls_mid_size"] = None
3439
- self.components.create_port_on_component(
3440
- cmp["refdes"],
3441
- net_list=simulation_setup.signal_nets,
3442
- do_pingroup=False,
3443
- reference_net=simulation_setup.power_nets,
3444
- port_type=source_type,
3445
- solder_balls_height=cmp["solder_balls_height"],
3446
- solder_balls_size=cmp["solder_balls_size"],
3447
- solder_balls_mid_size=cmp["solder_balls_mid_size"],
3448
- )
3449
- if simulation_setup.generate_solder_balls and not self.hfss.set_coax_port_attributes(
3450
- simulation_setup
3451
- ): # pragma: no cover
3452
- self.logger.error("Failed to configure coaxial port attributes.")
3453
- self.logger.info("Number of ports: {}".format(self.hfss.get_ports_number()))
3454
- self.logger.info("Configure HFSS extents.")
3455
- if (
3456
- simulation_setup.generate_solder_balls and simulation_setup.trim_reference_size
3457
- ): # pragma: no cover
3458
- self.logger.info(
3459
- "Trimming the reference plane for coaxial ports: {0}".format(
3460
- bool(simulation_setup.trim_reference_size)
3461
- )
3372
+ if simulation_setup.generate_solder_balls and not self.hfss.set_coax_port_attributes(
3373
+ simulation_setup
3374
+ ): # pragma: no cover
3375
+ self.logger.error("Failed to configure coaxial port attributes.")
3376
+ self.logger.info("Number of ports: {}".format(self.hfss.get_ports_number()))
3377
+ self.logger.info("Configure HFSS extents.")
3378
+ if simulation_setup.generate_solder_balls and simulation_setup.trim_reference_size: # pragma: no cover
3379
+ self.logger.info(
3380
+ "Trimming the reference plane for coaxial ports: {0}".format(
3381
+ bool(simulation_setup.trim_reference_size)
3382
+ )
3383
+ )
3384
+ self.hfss.trim_component_reference_size(simulation_setup) # pragma: no cover
3385
+ self.hfss.configure_hfss_extents(simulation_setup)
3386
+ if not self.hfss.configure_hfss_analysis_setup(simulation_setup):
3387
+ self.logger.error("Failed to configure HFSS simulation setup.")
3388
+ if simulation_setup.solver_type == SolverType.SiwaveSYZ:
3389
+ if simulation_setup.generate_excitations:
3390
+ for cmp in simulation_setup.components:
3391
+ if isinstance(cmp, str): # keep legacy
3392
+ self.components.create_port_on_component(
3393
+ cmp,
3394
+ net_list=simulation_setup.signal_nets,
3395
+ do_pingroup=simulation_setup.do_pingroup,
3396
+ reference_net=simulation_setup.power_nets,
3397
+ port_type=SourceType.CircPort,
3462
3398
  )
3463
- self.hfss.trim_component_reference_size(simulation_setup) # pragma: no cover
3464
- self.hfss.configure_hfss_extents(simulation_setup)
3465
- if not self.hfss.configure_hfss_analysis_setup(simulation_setup):
3466
- self.logger.error("Failed to configure HFSS simulation setup.")
3467
- if simulation_setup.solver_type == SolverType.SiwaveSYZ:
3468
- if simulation_setup.generate_excitations:
3469
- for cmp in simulation_setup.components:
3470
- if isinstance(cmp, str): # keep legacy
3399
+ elif isinstance(cmp, dict):
3400
+ if "refdes" in cmp: # pragma no cover
3471
3401
  self.components.create_port_on_component(
3472
- cmp,
3402
+ cmp["refdes"],
3473
3403
  net_list=simulation_setup.signal_nets,
3474
3404
  do_pingroup=simulation_setup.do_pingroup,
3475
3405
  reference_net=simulation_setup.power_nets,
3476
3406
  port_type=SourceType.CircPort,
3477
3407
  )
3478
- elif isinstance(cmp, dict):
3479
- if "refdes" in cmp: # pragma no cover
3480
- self.components.create_port_on_component(
3481
- cmp["refdes"],
3482
- net_list=simulation_setup.signal_nets,
3483
- do_pingroup=simulation_setup.do_pingroup,
3484
- reference_net=simulation_setup.power_nets,
3485
- port_type=SourceType.CircPort,
3486
- )
3487
- self.logger.info("Configuring analysis setup.")
3488
- if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
3489
- self.logger.error("Failed to configure Siwave simulation setup.")
3490
- if simulation_setup.solver_type == SolverType.SiwaveDC:
3491
- if simulation_setup.generate_excitations:
3492
- self.components.create_source_on_component(simulation_setup.sources)
3493
- if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
3494
- self.logger.error("Failed to configure Siwave simulation setup.")
3495
- self.padstacks.check_and_fix_via_plating()
3496
- self.save_edb()
3497
- if not simulation_setup.open_edb_after_build and simulation_setup.output_aedb:
3498
- self.close_edb()
3499
- self.edbpath = legacy_name
3500
- self.open_edb()
3501
- return True
3502
- except:
3503
- return False
3408
+ self.logger.info("Configuring analysis setup.")
3409
+ if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
3410
+ self.logger.error("Failed to configure Siwave simulation setup.")
3411
+ if simulation_setup.solver_type == SolverType.SiwaveDC:
3412
+ if simulation_setup.generate_excitations:
3413
+ self.components.create_source_on_component(simulation_setup.sources)
3414
+ if not self.siwave.configure_siw_analysis_setup(simulation_setup): # pragma: no cover
3415
+ self.logger.error("Failed to configure Siwave simulation setup.")
3416
+ self.padstacks.check_and_fix_via_plating()
3417
+ self.save_edb()
3418
+ if not simulation_setup.open_edb_after_build and simulation_setup.output_aedb:
3419
+ self.close_edb()
3420
+ self.edbpath = legacy_name
3421
+ self.open_edb()
3422
+ return True
3504
3423
 
3505
3424
  def get_statistics(self, compute_area=False):
3506
3425
  """Get the EDBStatistics object.
@@ -4178,6 +4097,12 @@ class Edb(Database):
4178
4097
  material_filter=None,
4179
4098
  padstack_definition_filter=None,
4180
4099
  trace_net_filter=None,
4100
+ use_single_variable_for_padstack_definitions=True,
4101
+ use_relative_variables=True,
4102
+ output_aedb_path=None,
4103
+ open_aedb_at_end=True,
4104
+ expand_polygons_size=0,
4105
+ expand_voids_size=0,
4181
4106
  ):
4182
4107
  """Assign automatically design and project variables with current values.
4183
4108
 
@@ -4203,27 +4128,56 @@ class Edb(Database):
4203
4128
  Enable padstack definition filter. Default value is ``None``, all padsatcks are parametrized.
4204
4129
  trace_net_filter : str, List(str), optional
4205
4130
  Enable nets filter for trace width parametrization. Default value is ``None``, all layers are parametrized.
4131
+ use_single_variable_for_padstack_definitions : bool, optional
4132
+ Whether to use a single design variable for each padstack definition or a variable per pad layer.
4133
+ Default value is ``True``.
4134
+ use_relative_variables : bool, optional
4135
+ Whether if use an absolute variable for each trace, padstacks and layers or a delta variable instead.
4136
+ Default value is ``True``.
4137
+ output_aedb_path : str, optional
4138
+ Full path and name for the new AEDB file. If None, then current aedb will be cutout.
4139
+ open_aedb_at_end : bool, optional
4140
+ Whether to open the cutout at the end. The default is ``True``.
4206
4141
 
4207
4142
  Returns
4208
4143
  -------
4209
4144
  List(str)
4210
4145
  List of all parameters name created.
4211
4146
  """
4147
+ edb_original_path = self.edbpath
4148
+ if output_aedb_path:
4149
+ self.save_edb_as(output_aedb_path)
4150
+ if isinstance(trace_net_filter, str):
4151
+ trace_net_filter = [trace_net_filter]
4212
4152
  parameters = []
4153
+
4154
+ def _apply_variable(orig_name, orig_value):
4155
+ if use_relative_variables:
4156
+ var = f"{orig_name}_delta"
4157
+ else:
4158
+ var = f"{orig_name}_value"
4159
+ var = self._clean_string_for_variable_name(var)
4160
+ if var not in self.variables:
4161
+ if use_relative_variables:
4162
+ self.add_design_variable(var, 0.0)
4163
+ else:
4164
+ self.add_design_variable(var, orig_value)
4165
+ if use_relative_variables:
4166
+ return f"{orig_value}+{var}", var
4167
+ else:
4168
+ return var, var
4169
+
4213
4170
  if layers:
4214
4171
  if not layer_filter:
4215
- _layers = self.stackup.stackup_layers
4172
+ _layers = self.stackup.layers
4216
4173
  else:
4217
4174
  if isinstance(layer_filter, str):
4218
4175
  layer_filter = [layer_filter]
4219
- _layers = {k: v for k, v in self.stackup.stackup_layers.items() if k in layer_filter}
4176
+ _layers = {k: v for k, v in self.stackup.layers.items() if k in layer_filter}
4220
4177
  for layer_name, layer in _layers.items():
4221
- thickness_variable = "${}_thick".format(layer_name)
4222
- thickness_variable = self._clean_string_for_variable_name(thickness_variable)
4223
- if thickness_variable not in self.variables:
4224
- self.add_design_variable(thickness_variable, layer.thickness)
4225
- layer.thickness = thickness_variable
4226
- parameters.append(thickness_variable)
4178
+ var, val = _apply_variable(f"${layer_name}", layer.thickness)
4179
+ layer.thickness = var
4180
+ parameters.append(val)
4227
4181
  if materials:
4228
4182
  if not material_filter:
4229
4183
  _materials = self.materials.materials
@@ -4231,117 +4185,139 @@ class Edb(Database):
4231
4185
  _materials = {k: v for k, v in self.materials.materials.items() if k in material_filter}
4232
4186
  for mat_name, material in _materials.items():
4233
4187
  if material.conductivity < 1e4:
4234
- epsr_variable = "$epsr_{}".format(mat_name)
4235
- epsr_variable = self._clean_string_for_variable_name(epsr_variable)
4236
- if epsr_variable not in self.variables:
4237
- self.add_design_variable(epsr_variable, material.permittivity)
4238
- material.permittivity = epsr_variable
4239
- parameters.append(epsr_variable)
4240
- loss_tg_variable = "$loss_tangent_{}".format(mat_name)
4241
- loss_tg_variable = self._clean_string_for_variable_name(loss_tg_variable)
4242
- if not loss_tg_variable in self.variables:
4243
- self.add_design_variable(loss_tg_variable, material.dielectric_loss_tangent)
4244
- material.dielectric_loss_tangent = loss_tg_variable
4245
- parameters.append(loss_tg_variable)
4188
+ var, val = _apply_variable(f"$epsr_{mat_name}", material.permittivity)
4189
+ material.permittivity = var
4190
+ parameters.append(val)
4191
+ var, val = _apply_variable(f"$loss_tangent_{mat_name}", material.dielectric_loss_tangent)
4192
+ material.dielectric_loss_tangent = var
4193
+ parameters.append(val)
4246
4194
  else:
4247
- sigma_variable = "$sigma_{}".format(mat_name)
4248
- sigma_variable = self._clean_string_for_variable_name(sigma_variable)
4249
- if not sigma_variable in self.variables:
4250
- self.add_design_variable(sigma_variable, material.conductivity)
4251
- material.conductivity = sigma_variable
4252
- parameters.append(sigma_variable)
4195
+ var, val = _apply_variable(f"$sigma_{mat_name}", material.conductivity)
4196
+ material.conductivity = var
4197
+ parameters.append(val)
4253
4198
  if traces:
4254
4199
  if not trace_net_filter:
4255
4200
  paths = self.modeler.paths
4256
4201
  else:
4257
4202
  paths = [path for path in self.modeler.paths if path.net_name in trace_net_filter]
4258
4203
  for path in paths:
4259
- trace_width_variable = "trace_w_{}_{}".format(path.net_name, path.id)
4260
- trace_width_variable = self._clean_string_for_variable_name(trace_width_variable)
4261
- if trace_width_variable not in self.variables:
4262
- self.add_design_variable(trace_width_variable, path.width)
4263
- path.width = trace_width_variable
4264
- parameters.append(trace_width_variable)
4204
+ net_name = path.net_name
4205
+ if use_relative_variables:
4206
+ trace_width_variable = "trace"
4207
+ elif net_name:
4208
+ trace_width_variable = f"{path.net_name}_{path.aedt_name}"
4209
+ else:
4210
+ trace_width_variable = f"{path.aedt_name}"
4211
+ var, val = _apply_variable(trace_width_variable, path.width)
4212
+ path.width = var
4213
+ parameters.append(val)
4265
4214
  if not padstack_definition_filter:
4266
- used_padsatck_defs = list(
4267
- set([padstack_inst.padstack_definition for padstack_inst in list(self.padstacks.instances.values())])
4268
- )
4269
- padstack_defs = {k: v for k, v in self.padstacks.definitions.items() if k in used_padsatck_defs}
4215
+ if trace_net_filter:
4216
+ padstack_defs = {}
4217
+ for net in trace_net_filter:
4218
+ for via in self.nets[net].padstack_instances:
4219
+ padstack_defs[via.padstack_definition] = self.padstacks.definitions[via.padstack_definition]
4220
+ else:
4221
+ used_padsatck_defs = list(
4222
+ set(
4223
+ [padstack_inst.padstack_definition for padstack_inst in list(self.padstacks.instances.values())]
4224
+ )
4225
+ )
4226
+ padstack_defs = {k: v for k, v in self.padstacks.definitions.items() if k in used_padsatck_defs}
4270
4227
  else:
4271
4228
  padstack_defs = {k: v for k, v in self.padstacks.definitions.items() if k in padstack_definition_filter}
4229
+
4272
4230
  for def_name, padstack_def in padstack_defs.items():
4273
4231
  if not padstack_def.via_start_layer == padstack_def.via_stop_layer:
4274
4232
  if via_holes: # pragma no cover
4275
- hole_variable = self._clean_string_for_variable_name("$hole_diam_{}".format(def_name))
4276
- if hole_variable not in self.variables:
4277
- self.add_design_variable(hole_variable, padstack_def.hole_diameter_string)
4278
- padstack_def.hole_properties = hole_variable
4279
- parameters.append(hole_variable)
4233
+ if use_relative_variables:
4234
+ hole_variable = "$hole_diameter"
4235
+ else:
4236
+ hole_variable = f"${def_name}_hole_diameter"
4237
+ var, val = _apply_variable(hole_variable, padstack_def.hole_diameter_string)
4238
+ padstack_def.hole_properties = var
4239
+ parameters.append(val)
4280
4240
  if pads:
4281
4241
  for layer, pad in padstack_def.pad_by_layer.items():
4282
- if pad.geometry_type == 1:
4283
- pad_diameter_variable = self._clean_string_for_variable_name(
4284
- "$pad_diam_{}_{}".format(def_name, layer)
4285
- )
4286
- if pad_diameter_variable not in self.variables:
4287
- self.add_design_variable(pad_diameter_variable, pad.parameters_values_string[0])
4288
- pad.parameters = {"Diameter": pad_diameter_variable}
4289
- parameters.append(pad_diameter_variable)
4290
- if pad.geometry_type == 2: # pragma no cover
4291
- pad_size_variable = self._clean_string_for_variable_name(
4292
- "$pad_size_{}_{}".format(def_name, layer)
4293
- )
4294
- if pad_size_variable not in self.variables:
4295
- self.add_design_variable(pad_size_variable, pad.parameters_values_string[0])
4296
- pad.parameters = {"Size": pad_size_variable}
4297
- parameters.append(pad_size_variable)
4242
+ if use_relative_variables:
4243
+ pad_name = "$pad"
4244
+ elif use_single_variable_for_padstack_definitions:
4245
+ pad_name = f"${def_name}_pad"
4246
+ else:
4247
+ pad_name = f"${def_name}_{layer}_pad"
4248
+
4249
+ if pad.geometry_type in [1, 2]:
4250
+ var, val = _apply_variable(pad_name, pad.parameters_values_string[0])
4251
+ if pad.geometry_type == 1:
4252
+ pad.parameters = {"Diameter": var}
4253
+ else:
4254
+ pad.parameters = {"Size": var}
4255
+ parameters.append(val)
4298
4256
  elif pad.geometry_type == 3: # pragma no cover
4299
- pad_size_variable_x = self._clean_string_for_variable_name(
4300
- "$pad_size_x_{}_{}".format(def_name, layer)
4301
- )
4302
- pad_size_variable_y = self._clean_string_for_variable_name(
4303
- "$pad_size_y_{}_{}".format(def_name, layer)
4304
- )
4305
- if pad_size_variable_x not in self.variables and pad_size_variable_y not in self.variables:
4306
- self.add_design_variable(pad_size_variable_x, pad.parameters_values_string[0])
4307
- self.add_design_variable(pad_size_variable_y, pad.parameters_values_string[1])
4308
- pad.parameters = {"XSize": pad_size_variable_x, "YSize": pad_size_variable_y}
4309
- parameters.append(pad_size_variable_x)
4310
- parameters.append(pad_size_variable_y)
4257
+ if use_relative_variables:
4258
+ pad_name_x = "$pad_x"
4259
+ pad_name_y = "$pad_y"
4260
+ elif use_single_variable_for_padstack_definitions:
4261
+ pad_name_x = f"${def_name}_pad_x"
4262
+ pad_name_y = f"${def_name}_pad_y"
4263
+ else:
4264
+ pad_name_x = f"${def_name}_{layer}_pad_x"
4265
+ pad_name_y = f"${def_name}_pad_y"
4266
+ var, val = _apply_variable(pad_name_x, pad.parameters_values_string[0])
4267
+ var2, val2 = _apply_variable(pad_name_y, pad.parameters_values_string[1])
4268
+
4269
+ pad.parameters = {"XSize": var, "YSize": var2}
4270
+ parameters.append(val)
4271
+ parameters.append(val2)
4311
4272
  if antipads:
4312
4273
  for layer, antipad in padstack_def.antipad_by_layer.items():
4313
- if antipad.geometry_type == 1: # pragma no cover
4314
- antipad_diameter_variable = self._clean_string_for_variable_name(
4315
- "$antipad_diam_{}_{}".format(def_name, layer)
4316
- )
4317
- if antipad_diameter_variable not in self.variables: # pragma no cover
4318
- self.add_design_variable(antipad_diameter_variable, antipad.parameters_values_string[0])
4319
- antipad.parameters = {"Diameter": antipad_diameter_variable}
4320
- parameters.append(antipad_diameter_variable)
4321
- if antipad.geometry_type == 2: # pragma no cover
4322
- antipad_size_variable = self._clean_string_for_variable_name(
4323
- "$antipad_size_{}_{}".format(def_name, layer)
4324
- )
4325
- if antipad_size_variable not in self.variables: # pragma no cover
4326
- self.add_design_variable(antipad_size_variable, antipad.parameters_values_string[0])
4327
- antipad.parameters = {"Size": antipad_size_variable}
4328
- parameters.append(antipad_size_variable)
4274
+ if use_relative_variables:
4275
+ pad_name = "$antipad"
4276
+ elif use_single_variable_for_padstack_definitions:
4277
+ pad_name = f"${def_name}_antipad"
4278
+ else:
4279
+ pad_name = f"${def_name}_{layer}_antipad"
4280
+
4281
+ if antipad.geometry_type in [1, 2]:
4282
+ var, val = _apply_variable(pad_name, antipad.parameters_values_string[0])
4283
+ if antipad.geometry_type == 1: # pragma no cover
4284
+ antipad.parameters = {"Diameter": var}
4285
+ else:
4286
+ antipad.parameters = {"Size": var}
4287
+ parameters.append(val)
4329
4288
  elif antipad.geometry_type == 3: # pragma no cover
4330
- antipad_size_variable_x = self._clean_string_for_variable_name(
4331
- "$antipad_size_x_{}_{}".format(def_name, layer)
4332
- )
4333
- antipad_size_variable_y = self._clean_string_for_variable_name(
4334
- "$antipad_size_y_{}_{}".format(def_name, layer)
4335
- )
4336
- if (
4337
- antipad_size_variable_x not in self.variables
4338
- and antipad_size_variable_y not in self.variables
4339
- ): # pragma no cover
4340
- self.add_design_variable(antipad_size_variable_x, antipad.parameters_values_string[0])
4341
- self.add_design_variable(antipad_size_variable_y, antipad.parameters_values_string[1])
4342
- antipad.parameters = {"XSize": antipad_size_variable_x, "YSize": antipad_size_variable_y}
4343
- parameters.append(antipad_size_variable_x)
4344
- parameters.append(antipad_size_variable_y)
4289
+ if use_relative_variables:
4290
+ pad_name_x = "$antipad_x"
4291
+ pad_name_y = "$antipad_y"
4292
+ elif use_single_variable_for_padstack_definitions:
4293
+ pad_name_x = f"${def_name}_antipad_x"
4294
+ pad_name_y = f"${def_name}_antipad_y"
4295
+ else:
4296
+ pad_name_x = f"${def_name}_{layer}_antipad_x"
4297
+ pad_name_y = f"${def_name}_antipad_y"
4298
+
4299
+ var, val = _apply_variable(pad_name_x, antipad.parameters_values_string[0])
4300
+ var2, val2 = _apply_variable(pad_name_y, antipad.parameters_values_string[1])
4301
+ antipad.parameters = {"XSize": var, "YSize": var2}
4302
+ parameters.append(val)
4303
+ parameters.append(val2)
4304
+ if expand_polygons_size:
4305
+ for poly in self.modeler.polygons:
4306
+ if not poly.is_void:
4307
+ poly.expand(expand_polygons_size)
4308
+ if expand_voids_size:
4309
+ for poly in self.modeler.polygons:
4310
+ if poly.is_void:
4311
+ poly.expand(expand_voids_size, round_corners=False)
4312
+ elif poly.has_voids:
4313
+ for void in poly.voids:
4314
+ void.expand(expand_voids_size, round_corners=False)
4315
+
4316
+ if not open_aedb_at_end and self.edbpath != edb_original_path:
4317
+ self.save_edb()
4318
+ self.close_edb()
4319
+ self.edbpath = edb_original_path
4320
+ self.open_edb()
4345
4321
  return parameters
4346
4322
 
4347
4323
  def _clean_string_for_variable_name(self, variable_name):