pyedb 0.58.0__py3-none-any.whl → 0.59.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyedb might be problematic. Click here for more details.

pyedb/__init__.py CHANGED
@@ -37,7 +37,7 @@ deprecation_warning()
37
37
  #
38
38
 
39
39
  pyedb_path = os.path.dirname(__file__)
40
- __version__ = "0.58.0"
40
+ __version__ = "0.59.0"
41
41
  version = __version__
42
42
 
43
43
  #
@@ -1011,49 +1011,47 @@ class Components(object):
1011
1011
  return False
1012
1012
  pad_params = self._padstack.get_pad_parameters(pin=cmp_pins[0], layername=pin_layers[0], pad_type=0)
1013
1013
 
1014
- # If at least one of the solderball arguments is not None, calculate the rest and set solderballs
1015
- if not (not solder_balls_height and not solder_balls_size and not solder_balls_mid_size):
1016
- if not solder_balls_height:
1017
- solder_balls_height = self.instances[component.GetName()].solder_ball_height
1018
- if not solder_balls_size:
1019
- solder_balls_size = self.instances[component.GetName()].solder_ball_diameter[0]
1020
- if not solder_balls_mid_size:
1021
- solder_balls_mid_size = self.instances[component.GetName()].solder_ball_diameter[1]
1022
-
1023
- if not pad_params[0] == 7:
1024
- if not solder_balls_size: # pragma no cover
1025
- sball_diam = min([self._pedb.edb_value(val).ToDouble() for val in pad_params[1]])
1026
- sball_mid_diam = sball_diam
1027
- else: # pragma no cover
1028
- sball_diam = solder_balls_size
1029
- if solder_balls_mid_size:
1030
- sball_mid_diam = solder_balls_mid_size
1031
- else:
1032
- sball_mid_diam = solder_balls_size
1033
- if not solder_balls_height: # pragma no cover
1034
- solder_balls_height = 2 * sball_diam / 3
1014
+ if not solder_balls_height:
1015
+ solder_balls_height = self.instances[component.GetName()].solder_ball_height
1016
+ if not solder_balls_size:
1017
+ solder_balls_size = self.instances[component.GetName()].solder_ball_diameter[0]
1018
+ if not solder_balls_mid_size:
1019
+ solder_balls_mid_size = self.instances[component.GetName()].solder_ball_diameter[1]
1020
+
1021
+ if not pad_params[0] == 7:
1022
+ if not solder_balls_size: # pragma no cover
1023
+ sball_diam = min([self._pedb.edb_value(val).ToDouble() for val in pad_params[1]])
1024
+ sball_mid_diam = sball_diam
1035
1025
  else: # pragma no cover
1036
- if not solder_balls_size:
1037
- bbox = pad_params[1]
1038
- sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
1039
- else:
1040
- sball_diam = solder_balls_size
1041
- if not solder_balls_height:
1042
- solder_balls_height = 2 * sball_diam / 3
1026
+ sball_diam = solder_balls_size
1043
1027
  if solder_balls_mid_size:
1044
1028
  sball_mid_diam = solder_balls_mid_size
1045
1029
  else:
1046
- sball_mid_diam = sball_diam
1047
- sball_shape = "Cylinder"
1048
- if not sball_diam == sball_mid_diam:
1049
- sball_shape = "Spheroid"
1050
- self.set_solder_ball(
1051
- component=component,
1052
- sball_height=solder_balls_height,
1053
- sball_diam=sball_diam,
1054
- sball_mid_diam=sball_mid_diam,
1055
- shape=sball_shape,
1056
- )
1030
+ sball_mid_diam = solder_balls_size
1031
+ if not solder_balls_height: # pragma no cover
1032
+ solder_balls_height = 2 * sball_diam / 3
1033
+ else: # pragma no cover
1034
+ if not solder_balls_size:
1035
+ bbox = pad_params[1]
1036
+ sball_diam = min([abs(bbox[2] - bbox[0]), abs(bbox[3] - bbox[1])]) * 0.8
1037
+ else:
1038
+ sball_diam = solder_balls_size
1039
+ if not solder_balls_height:
1040
+ solder_balls_height = 2 * sball_diam / 3
1041
+ if solder_balls_mid_size:
1042
+ sball_mid_diam = solder_balls_mid_size
1043
+ else:
1044
+ sball_mid_diam = sball_diam
1045
+ sball_shape = "Cylinder"
1046
+ if not sball_diam == sball_mid_diam:
1047
+ sball_shape = "Spheroid"
1048
+ self.set_solder_ball(
1049
+ component=component,
1050
+ sball_height=solder_balls_height,
1051
+ sball_diam=sball_diam,
1052
+ sball_mid_diam=sball_mid_diam,
1053
+ shape=sball_shape,
1054
+ )
1057
1055
 
1058
1056
  for pin in cmp_pins:
1059
1057
  self._padstack.create_coax_port(padstackinstance=pin, name=port_name)
@@ -51,8 +51,9 @@ def convert_netdict_to_pydict(dict_in):
51
51
 
52
52
  """
53
53
  pydict = {}
54
- for key in dict_in.Keys:
55
- pydict[key] = dict_in[key]
54
+ if dict_in:
55
+ for key in dict_in.Keys:
56
+ pydict[key] = dict_in[key]
56
57
  return pydict
57
58
 
58
59
 
@@ -579,14 +579,14 @@ class ViaSettings(object):
579
579
  -------
580
580
  bool
581
581
  """
582
- if float(self._parent._pedb.edbversion) < 2024.1:
582
+ if float(self._parent._pedb.version) < 2024.1:
583
583
  self._parent._pedb.logger.error("Property only supported on Ansys release 2024R1 and later")
584
584
  return False
585
585
  return self._via_settings.ViaMeshPlating
586
586
 
587
587
  @via_mesh_plating.setter
588
588
  def via_mesh_plating(self, value):
589
- if float(self._parent._pedb.edbversion) < 2024.1:
589
+ if float(self._parent._pedb.version) < 2024.1:
590
590
  self._parent._pedb.logger.error("Property only supported on Ansys release 2024R1 and later")
591
591
  else:
592
592
  self._via_settings.ViaMeshPlating = value
pyedb/grpc/edb.py CHANGED
@@ -217,8 +217,8 @@ class Edb(EdbInit):
217
217
  self.standalone = True
218
218
  self.oproject = oproject
219
219
  self._main = sys.modules["__main__"]
220
- self.edbversion = edbversion
221
- if not float(self.edbversion) >= 2025.2:
220
+ self.version = edbversion
221
+ if not float(self.version) >= 2025.2:
222
222
  raise "EDB gRPC is only supported with ANSYS release 2025R2 and higher."
223
223
  self.logger.info("Using PyEDB with gRPC as Beta until ANSYS 2025R2 official release.")
224
224
  self.isaedtowned = isaedtowned
@@ -620,7 +620,7 @@ class Edb(EdbInit):
620
620
  if self.db.is_null:
621
621
  self.logger.warning("Error Opening db")
622
622
  self._active_cell = None
623
- self.logger.info(f"Database {os.path.split(self.edbpath)[-1]} Opened in {self.edbversion}")
623
+ self.logger.info(f"Database {os.path.split(self.edbpath)[-1]} Opened in {self.version}")
624
624
  self._active_cell = None
625
625
  if self.cellname:
626
626
  for cell in self.active_db.circuit_cells:
@@ -1371,7 +1371,7 @@ class Edb(EdbInit):
1371
1371
  Layer filter file.
1372
1372
  """
1373
1373
  control_file_temp = os.path.join(tempfile.gettempdir(), os.path.split(inputGDS)[-1][:-3] + "xml")
1374
- if float(self.edbversion) < 2024.1:
1374
+ if float(self.version) < 2024.1:
1375
1375
  if not is_linux and tech_file:
1376
1376
  self.logger.error("Technology files are supported only in Linux. Use control file instead.")
1377
1377
  return False
@@ -3062,7 +3062,7 @@ class Edb(EdbInit):
3062
3062
  if name in self.setups:
3063
3063
  self.logger.error("Setup name already used in the layout")
3064
3064
  return False
3065
- version = self.edbversion.split(".")
3065
+ version = self.version.split(".")
3066
3066
  if int(version[0]) >= 2024 and int(version[-1]) >= 2 or int(version[0]) > 2024:
3067
3067
  setup = GrpcRaptorXSimulationSetup.create(cell=self.active_cell, name=name)
3068
3068
  return RaptorXSimulationSetup(self, setup)
@@ -3253,7 +3253,7 @@ class Edb(EdbInit):
3253
3253
  defined_ports = {}
3254
3254
  project_connexions = None
3255
3255
  for edb_path, zone_info in zone_dict.items():
3256
- edb = Edb(edbversion=self.edbversion, edbpath=edb_path)
3256
+ edb = Edb(edbversion=self.version, edbpath=edb_path)
3257
3257
  edb.cutout(
3258
3258
  use_pyaedt_cutout=True,
3259
3259
  custom_extent=zone_info[1],
@@ -3795,7 +3795,7 @@ class Edb(EdbInit):
3795
3795
  "No padstack instances found inside evaluated voids during model creation for arbitrary waveports"
3796
3796
  )
3797
3797
  return False
3798
- cloned_edb = Edb(edbpath=output_edb, edbversion=self.edbversion, restart_rpc_server=True)
3798
+ cloned_edb = Edb(edbpath=output_edb, edbversion=self.version, restart_rpc_server=True)
3799
3799
 
3800
3800
  cloned_edb.stackup.add_layer(
3801
3801
  layer_name="ports",
pyedb/grpc/edb_init.py CHANGED
@@ -43,23 +43,23 @@ class EdbInit(object):
43
43
  def __init__(self, edbversion):
44
44
  self.logger = settings.logger
45
45
  self._db = None
46
- self.edbversion = edbversion
46
+ self.version = edbversion
47
47
  self.logger.info("Logger is initialized in EDB.")
48
48
  self.logger.info("legacy v%s", __version__)
49
49
  self.logger.info("Python version %s", sys.version)
50
50
  self.session = None
51
51
  if is_linux:
52
- if env_value(self.edbversion) in os.environ:
53
- self.base_path = env_path(self.edbversion)
52
+ if env_value(self.version) in os.environ:
53
+ self.base_path = env_path(self.version)
54
54
  sys.path.append(self.base_path)
55
55
  else:
56
56
  edb_path = os.getenv("PYAEDT_SERVER_AEDT_PATH")
57
57
  if edb_path:
58
58
  self.base_path = edb_path
59
59
  sys.path.append(edb_path)
60
- os.environ[env_value(self.edbversion)] = self.base_path
60
+ os.environ[env_value(self.version)] = self.base_path
61
61
  else:
62
- self.base_path = env_path(self.edbversion)
62
+ self.base_path = env_path(self.version)
63
63
  sys.path.append(self.base_path)
64
64
  os.environ["ECAD_TRANSLATORS_INSTALL_DIR"] = self.base_path
65
65
  oa_directory = os.path.join(self.base_path, "common", "oa")
@@ -100,7 +100,7 @@ class EdbInit(object):
100
100
  """
101
101
  if not RpcSession.pid:
102
102
  RpcSession.start(
103
- edb_version=self.edbversion,
103
+ edb_version=self.version,
104
104
  port=port,
105
105
  restart_server=restart_rpc_server,
106
106
  )
@@ -133,7 +133,7 @@ class EdbInit(object):
133
133
  RpcSession.pid = 0
134
134
  if not RpcSession.pid:
135
135
  RpcSession.start(
136
- edb_version=self.edbversion,
136
+ edb_version=self.version,
137
137
  port=port,
138
138
  restart_server=restart_rpc_server,
139
139
  )
@@ -372,18 +372,6 @@ class EdbInit(object):
372
372
  """
373
373
  self._db.import_material_from_control_file(control_file, schema_dir, append)
374
374
 
375
- @property
376
- def version(self):
377
- """Get version of the Database.
378
-
379
- Returns
380
- -------
381
- tuple(int, int)
382
- A tuple of the version numbers [major, minor]
383
- """
384
- major, minor = self._db.version
385
- return major, minor
386
-
387
375
  def scale(self, scale_factor):
388
376
  """Uniformly scale all geometry and their locations by a positive factor.
389
377
 
@@ -0,0 +1,711 @@
1
+ # Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ from __future__ import annotations
24
+
25
+ from collections import defaultdict
26
+ from dataclasses import dataclass, field
27
+ import os
28
+ from pathlib import Path
29
+ import re
30
+ import shutil
31
+ import stat
32
+ from typing import Dict, List, Optional, Sequence, Tuple, Union
33
+
34
+ from pyedb import Edb
35
+
36
+ # patterns used for Regex matching of ground/reference nets
37
+ ref_patterns = [
38
+ r"^GND\d*$",
39
+ r"^GND_\w+",
40
+ r"^GND$",
41
+ r"^VSS\d*$",
42
+ r"^VSS\w*",
43
+ r"^DGND$",
44
+ r"^AGND$",
45
+ r"^PGND$",
46
+ r"^EGND$",
47
+ r"^SGND$",
48
+ r"^REF$",
49
+ r"^VREF[A-Z0-9]*",
50
+ r"^VREF$",
51
+ r"^VREF_\d+\.\d+V$",
52
+ r".*_REF$",
53
+ r".*REF$",
54
+ r"^VR[A-Z0-9]*",
55
+ r"^VTT$",
56
+ r"^VTT\d*V$",
57
+ r"^VDDQ_REF$",
58
+ r"^VPP$",
59
+ r"^VCCO_\w+",
60
+ r"^VCCA_\w+",
61
+ r"^VCCD_\w+",
62
+ r"^VSYS$",
63
+ r"^VBUS$",
64
+ r"^0V$",
65
+ r"^0V_\w+",
66
+ r"^GND plane$",
67
+ r"^GROUND$",
68
+ r"^SENSE\d*$",
69
+ r"^KSENSE\w*",
70
+ r"^CAL\d*$",
71
+ r"^CAL_\w+",
72
+ r"^VCM\d*$",
73
+ r"^VCM\w+",
74
+ r"^BGREF$",
75
+ r"^BGVREF$",
76
+ r"^VREFP$",
77
+ r"^VREFN$",
78
+ r"^AVSS$",
79
+ r"^AVDD$",
80
+ r"^DVSS$",
81
+ r"^DVDD$",
82
+ ]
83
+
84
+ combined_ref = re.compile("|".join("(?:%s)" % p for p in ref_patterns), re.I)
85
+
86
+
87
+ @dataclass
88
+ class SolderBallsInfo:
89
+ ref_des: str = field(default="")
90
+ shape: str = field(default="cylinder")
91
+ diameter: Optional[Union[str, float]] = None
92
+ mid_diameter: Optional[Union[str, float]] = None
93
+ height: Optional[Union[str, float]] = None
94
+
95
+
96
+ @dataclass
97
+ class SimulationSetup:
98
+ meshing_frequency: Union[str, float] = field(default="10GHz")
99
+ maximum_pass_number: int = field(default=15)
100
+ start_frequency: Union[str, float] = field(default=0)
101
+ stop_frequency: Union[str, float] = field(default="40GHz")
102
+ frequency_step: Union[str, float] = field(default="0.05GHz")
103
+
104
+
105
+ @dataclass
106
+ class BatchGroup:
107
+ name: str = field(default="")
108
+ nets: List[str] = field(default_factory=list)
109
+ simulation_setup: SimulationSetup = None # if None, use default in auto config
110
+
111
+
112
+ class HFSSAutoConfiguration:
113
+ def __init__(self, edb=None):
114
+ self._pedb = edb
115
+ self.ansys_version: str = "2025.2"
116
+ self.grpc: bool = True
117
+ self.source_edb_path: str = ""
118
+ self.target_edb_path: str = ""
119
+ self.batch_group_folder: str = ""
120
+ self.signal_nets: list = []
121
+ self.power_nets: list = []
122
+ self.reference_net: str = ""
123
+ self.batch_size: int = 100
124
+ self.batch_groups: list[BatchGroup] = []
125
+ self.components: list[str] = []
126
+ self.solder_balls: list[SolderBallsInfo] = []
127
+ self.simulation_setup: SimulationSetup = SimulationSetup()
128
+ self.extent_type: str = "bounding_box"
129
+ self.cutout_expansion: Union[float, str] = "2mm"
130
+ self.auto_mesh_seeding: bool = True
131
+ self.port_type: str = "coaxial"
132
+ self.create_pin_group: bool = False
133
+
134
+ _DIFF_SUFFIX = re.compile(r"_[PN]$|_[ML]$|_[+-]$", re.I)
135
+
136
+ def auto_populate_batch_groups(
137
+ self,
138
+ pattern: str | list[str] | None = None,
139
+ ) -> None:
140
+ """
141
+ Automatically create and populate :attr:`batch_groups` from the current
142
+ :attr:`signal_nets`.
143
+
144
+ This is a thin convenience wrapper around :meth:`group_nets_by_prefix`.
145
+ It **only** executes when both:
146
+
147
+ * :attr:`auto_evaluate_batch_groups` is ``True``, and
148
+ * :attr:`signal_nets` is non-empty.
149
+
150
+ Parameters
151
+ ----------
152
+ pattern : :class:`str` | :class:`list` [:class:`str`] | ``None``, optional
153
+ POSIX ERE prefix pattern(s) that control which nets are grouped.
154
+
155
+ * ``None`` *(default)* – activate **auto-discovery** mode: nets are
156
+ clustered heuristically and then split into chunks of size
157
+ :attr:`batch_size`.
158
+ * :class:`str` – treat the single string as a prefix pattern
159
+ (automatically anchored: ``pattern + ".*"``).
160
+ * :class:`list` [:class:`str`] – each list element becomes its own
161
+ prefix pattern; one :class:`.BatchGroup` is created **per list
162
+ entry**, regardless of :attr:`batch_size`.
163
+
164
+ Side-effects
165
+ ------------
166
+ Clears and repopulates :attr:`batch_groups` in-place.
167
+ """
168
+ if not self._pedb:
169
+ self._pedb = Edb(edbpath=self.source_edb_path, version=self.ansys_version, grpc=self.grpc)
170
+ self.signal_nets = list(self._pedb.nets.signal.keys())
171
+ all_power_nets = list(self._pedb.nets.power.keys())
172
+ reference_nets = [n for n in all_power_nets if combined_ref.match(n)]
173
+
174
+ # --- guarantee: any net whose *upper-case* name contains "GND" comes first ---
175
+ def __key(n):
176
+ return (0, n) if "GND" in n.upper() else (1, n)
177
+
178
+ _ref_nets = list(sorted(reference_nets, key=__key))
179
+ if len(_ref_nets) > 1:
180
+ self._pedb.logger.warning(
181
+ f"Multiple candidate reference nets found: {_ref_nets}. Using {_ref_nets[0]} as the reference net."
182
+ )
183
+ self.reference_net = _ref_nets[0]
184
+ self.power_nets = [n for n in all_power_nets if n not in _ref_nets]
185
+ self.group_nets_by_prefix(pattern)
186
+ self._pedb.close(terminate_rpc_session=False)
187
+
188
+ def add_batch_group(
189
+ self,
190
+ name: str,
191
+ nets: Sequence[str] | None = None,
192
+ *,
193
+ simulation_setup: SimulationSetup | None = None,
194
+ ) -> BatchGroup:
195
+ """
196
+ Append a new BatchGroup to the configuration.
197
+
198
+ Parameters
199
+ ----------
200
+ name : str
201
+ Descriptive name for the group (will also become the regex
202
+ pattern when the group is built automatically).
203
+ nets : Sequence[str], optional
204
+ List of net names that belong to this batch. If omitted
205
+ an empty list is assumed and you can fill it later.
206
+ simulation_setup : SimulationSetup, optional
207
+ Per-batch simulation settings. When None the global
208
+ ``self.simulation_setup`` is used.
209
+
210
+ Returns
211
+ -------
212
+ BatchGroup
213
+ The freshly created instance (already appended to
214
+ ``self.batch_groups``) so the caller can further
215
+ manipulate it if desired.
216
+ """
217
+ bg = BatchGroup(
218
+ name=name,
219
+ nets=list(nets or []),
220
+ simulation_setup=simulation_setup,
221
+ )
222
+ self.batch_groups.append(bg)
223
+ return bg
224
+
225
+ def add_solder_ball(
226
+ self,
227
+ ref_des: str,
228
+ shape: str = "cylinder",
229
+ diameter: Optional[Union[str, float]] = None,
230
+ mid_diameter: Optional[Union[str, float]] = None,
231
+ height: Optional[Union[str, float]] = None,
232
+ ) -> SolderBallsInfo:
233
+ """
234
+ Append a new :class:`.SolderBallsInfo` entry to the configuration.
235
+
236
+ Parameters
237
+ ----------
238
+ ref_des : :class:`str`
239
+ Reference designator of the component to which the solder-ball
240
+ definition applies (e.g. ``"U1"``).
241
+ shape : :class:`str`, default ``"cylinder"``
242
+ Geometric model used for the solder ball. Supported values are
243
+ ``"cylinder"``, ``"sphere"``, ``"spheroid"``, etc.
244
+ diameter : :class:`str` | :class:`float` | ``None``, optional
245
+ Nominal diameter. When ``None`` HFSS auto-evaluates the value
246
+ from the footprint.
247
+ mid_diameter : :class:`str` | :class:`float` | ``None``, optional
248
+ Middle diameter **required only for spheroid shapes**. Ignored
249
+ for all other geometries.
250
+ height : :class:`str` | :class:`float` | ``None``, optional
251
+ Ball height. When ``None`` HFSS computes an appropriate value
252
+ automatically.
253
+
254
+ Returns
255
+ -------
256
+ :class:`.SolderBallsInfo`
257
+ The newly created instance (already appended to
258
+ :attr:`solder_balls`). The object can be further edited in-place
259
+ by the caller if desired.
260
+
261
+ Examples
262
+ --------
263
+ >>> cfg = HfssAutoConfig()
264
+ >>> cfg.add_solder_ball("U1", diameter="0.3mm", height="0.2mm")
265
+ >>> cfg.add_solder_ball(
266
+ ... "U2",
267
+ ... shape="spheroid",
268
+ ... diameter="0.25mm",
269
+ ... mid_diameter="0.35mm",
270
+ ... height="0.18mm",
271
+ ... )
272
+ """
273
+ sb = SolderBallsInfo(
274
+ ref_des=ref_des,
275
+ shape=shape,
276
+ diameter=diameter,
277
+ mid_diameter=mid_diameter,
278
+ height=height,
279
+ )
280
+ self.solder_balls.append(sb)
281
+ return sb
282
+
283
+ def add_simulation_setup(
284
+ self,
285
+ meshing_frequency: Optional[Union[str, float]] = "10GHz",
286
+ maximum_pass_number: int = 15,
287
+ start_frequency: Optional[Union[str, float]] = 0,
288
+ stop_frequency: Optional[Union[str, float]] = "40GHz",
289
+ frequency_step: Optional[Union[str, float]] = "0.05GHz",
290
+ replace: bool = True,
291
+ ) -> SimulationSetup:
292
+ r"""
293
+ Create a: class:`.SimulationSetup` instance and attach it to the configuration.
294
+
295
+ Parameters
296
+ ----------
297
+ meshing_frequency : Union[:class:`str`,: class:`float`], default ``"10GHz"``
298
+ Driven frequency used during mesh generation.
299
+ maximum_pass_number : class:`int`, default ``15``
300
+ Maximum number of adaptive passes.
301
+ start_frequency : Union[:class:`str`,: class:`float`], default ``0``
302
+ Lower bound of the sweep window.
303
+ stop_frequency : Union[:class:`str`,: class:`float`], default ``"40GHz"``
304
+ Upper bound of the sweep window.
305
+ frequency_step : Union[:class:`str`,: class:`float`], default ``"0.05GHz"``
306
+ Linear step size for the frequency sweep.
307
+ mesh_operation_size : Union[:class:`str`,: class:`float`, ``None``], optional
308
+ Maximum element size for mesh operations. When ``None`` HFSS
309
+ computes an appropriate value automatically.
310
+ replace : class:`bool`, default ``False``
311
+ Placement strategy for the new setup:
312
+
313
+ * ``False`` – append a *per-batch* setup by creating an auxiliary
314
+ :class:`.BatchGroup` (``name="extra_setup"``) whose
315
+ :attr:`.BatchGroup.simulation_setup` points to the new object.
316
+ * ``True`` – overwrite the **global**: attr:`simulation_setup`
317
+ attribute of the current :class:`.HfssAutoConfig` instance.
318
+
319
+ Returns
320
+ -------
321
+ :class:`.SimulationSetup`
322
+ The newly created instance (already stored inside the configuration).
323
+
324
+ Examples
325
+ --------
326
+ >>> cfg = HfssAutoConfig()
327
+ >>> # global setup
328
+ >>> cfg.add_simulation_setup(frequency_max="60GHz", replace=True)
329
+ >>> # per-batch setup
330
+ >>> cfg.add_simulation_setup(frequency_step="0.1GHz")
331
+ """
332
+ setup = SimulationSetup(
333
+ meshing_frequency=meshing_frequency,
334
+ maximum_pass_number=maximum_pass_number,
335
+ start_frequency=start_frequency,
336
+ stop_frequency=stop_frequency,
337
+ frequency_step=frequency_step,
338
+ )
339
+ if replace:
340
+ self.simulation_setup = setup
341
+ else:
342
+ self.batch_groups.append(BatchGroup(name="extra_setup", simulation_setup=setup))
343
+ return setup
344
+
345
+ @staticmethod
346
+ def _longest_common_prefix(strings: Sequence[str]) -> str:
347
+ if not strings:
348
+ return ""
349
+ normed = [re.sub(r"[^A-Za-z0-9_]", "", s).upper() for s in strings]
350
+ s_min, s_max = min(normed), max(normed)
351
+ idx = 0
352
+ while idx < len(s_min) and s_min[idx] == s_max[idx]:
353
+ idx += 1
354
+ return strings[0][:idx]
355
+
356
+ def _infer_prefix_patterns(self, nets: Sequence[str]) -> List[str]:
357
+ if not nets:
358
+ return []
359
+ total = len(nets)
360
+ min_group_size = max(1, int(0.05 * total))
361
+ groups: List[Tuple[str, List[str]]] = []
362
+ for net in sorted(nets):
363
+ if groups:
364
+ last_prefix, last_members = groups[-1]
365
+ trial_prefix = self._longest_common_prefix(last_members + [net])
366
+ if trial_prefix and len(last_members) + 1 >= min_group_size:
367
+ groups[-1] = (trial_prefix, last_members + [net])
368
+ continue
369
+ groups.append((net, [net]))
370
+ return [re.escape(pfx) + r".*" for pfx, _ in groups]
371
+
372
+ def _base_name(self, net: str) -> str:
373
+ return self._DIFF_SUFFIX.sub("", net)
374
+
375
+ def _build_diff_pairs(self, nets: Sequence[str]) -> List[Tuple[str, List[str]]]:
376
+ buckets: Dict[str, List[str]] = defaultdict(list)
377
+ for n in nets:
378
+ buckets[self._base_name(n)].append(n)
379
+ clusters = []
380
+ for base, members in buckets.items():
381
+ if len(members) >= 2 or not self._DIFF_SUFFIX.search(members[0]):
382
+ clusters.append((base, sorted(members)))
383
+ return clusters
384
+
385
+ def group_nets_by_prefix(
386
+ self,
387
+ prefix_patterns: Optional[Sequence[str]] = None,
388
+ ) -> Dict[str, List[List[str]]]:
389
+ r"""
390
+ Group signal nets into *disjoint* batches while preserving differential pairs.
391
+
392
+ Behaviour in a nutshell
393
+ -----------------------
394
+ 1. Nets that form differential pairs (``PCIe_RX0_P`` / ``PCIe_RX0_N``, ``USB3_TX_M`` / ``USB3_TX_P`` …)
395
+ are **never split**; they always appear in the **same** batch.
396
+ 2. Every net is assigned to **exactly one** batch.
397
+ 3. No batch contains only a single net; orphans are merged into the largest compatible group.
398
+ 4. When *prefix_patterns* is supplied **only** nets that match one of those patterns are
399
+ returned; everything else is silently ignored.
400
+ 5. If *prefix_patterns* is supplied the caller gets **one group per pattern** regardless of
401
+ :attr:`batch_size`; when it is ``None`` the legacy auto-discovery mode is used and
402
+ :attr:`batch_size` is honoured.
403
+
404
+ Parameters
405
+ ----------
406
+ prefix_patterns : Sequence[str], optional
407
+ POSIX ERE patterns that define the prefixes to be grouped.
408
+ Example: ``["PCIe", "USB"]`` ➜ interpreted as ``["PCIe.*", "USB.*"]``.
409
+ If ``None`` patterns are derived heuristically from the data set
410
+ (see :meth:`_infer_prefix_patterns`).
411
+
412
+ Returns
413
+ -------
414
+ Dict[str, List[List[str]]]
415
+ Keys are the original / generated pattern strings.
416
+ Values are lists of batches; each batch is an alphabetically sorted
417
+ list of net names. When *prefix_patterns* was supplied the list
418
+ contains **exactly one** element (the complete group); in auto-discovery
419
+ mode the list may contain multiple slices sized according to
420
+ :attr:`batch_size`.
421
+
422
+ Examples
423
+ --------
424
+ Explicit grouping (production intent)::
425
+
426
+ >>> cfg.signal_nets = ["PCIe_RX0_P", "PCIe_RX0_N", "PCIe_TX0_P",
427
+ ... "USB3_DP", "USB3_DN", "DDR4_A0", "DDR4_A1"]
428
+ >>> cfg.batch_size = 1_000 # ignored when patterns are supplied
429
+ >>> cfg.group_nets_by_prefix(["PCIe", "USB"])
430
+ {'PCIe.*': [['PCIe_RX0_N', 'PCIe_RX0_P', 'PCIe_TX0_P']],
431
+ 'USB.*': [['USB3_DN', 'USB3_DP']]}
432
+
433
+ Auto-discovery with batching::
434
+
435
+ >>> cfg.group_nets_by_prefix() # batch_size = 2
436
+ {'PCIe.*': [['PCIe_RX0_N', 'PCIe_RX0_P'], ['PCIe_TX0_P']],
437
+ 'USB.*': [['USB3_DN', 'USB3_DP']],
438
+ 'DDR4.*': [['DDR4_A0', 'DDR4_A1']]}
439
+
440
+ Notes
441
+ -----
442
+ * Differential recognition strips the suffixes ``_[PN]``, ``_[ML]``, ``_[+-]``
443
+ (case-insensitive).
444
+ * The function updates the instance attribute :attr:`batch_groups` in place.
445
+ """
446
+ if not self.signal_nets:
447
+ return {}
448
+
449
+ clusters = self._build_diff_pairs(self.signal_nets)
450
+
451
+ # ---------- 1. patterns ------------------------------------------
452
+ if prefix_patterns is None:
453
+ patterns = self._infer_prefix_patterns([base for base, _ in clusters])
454
+ else:
455
+ patterns = [p if p.endswith(".*") else p + ".*" for p in prefix_patterns]
456
+
457
+ compiled = [re.compile(p, re.I) for p in patterns]
458
+
459
+ # ---------- 2. bucket clusters ------------------------------------
460
+ buckets: Dict[str, List[Tuple[str, List[str]]]] = defaultdict(list)
461
+ for base, members in clusters:
462
+ for pat, orig in zip(compiled, patterns):
463
+ if pat.match(base):
464
+ buckets[orig].append((base, members))
465
+ break
466
+
467
+ # ---------- 3. flatten --------------------------------------------
468
+ flat: Dict[str, List[str]] = {}
469
+ for pat in patterns:
470
+ if pat not in buckets:
471
+ continue
472
+ flat[pat] = []
473
+ for _, members in buckets[pat]:
474
+ flat[pat].extend(members)
475
+ flat[pat].sort()
476
+
477
+ # ---------- 4. merge singles --------------------------------------
478
+ singles = [k for k, lst in flat.items() if len(lst) == 1]
479
+ if singles:
480
+ biggest = max(flat.keys(), key=lambda k: len(flat[k]))
481
+ for k in singles:
482
+ flat[biggest].extend(flat[k])
483
+ del flat[k]
484
+ flat[biggest].sort()
485
+
486
+ # ---------- 5. ONE group per supplied prefix -----------------------
487
+ grouped: Dict[str, List[List[str]]] = {}
488
+ for pat, lst in flat.items():
489
+ if prefix_patterns is None:
490
+ # old auto-mode – respect batch_size
491
+ if self.batch_size is None:
492
+ grouped[pat] = [lst]
493
+ else:
494
+ grouped[pat] = [lst[i : i + self.batch_size] for i in range(0, len(lst), self.batch_size)]
495
+ else:
496
+ # user-mode – exactly one group per requested prefix
497
+ grouped[pat] = [lst]
498
+
499
+ # ---------- 6. update instance -------------------------------------
500
+ self.batch_groups.clear()
501
+ for pat, batches in grouped.items():
502
+ for nets in batches:
503
+ self.batch_groups.append(BatchGroup(name=pat, nets=nets))
504
+ grouped = {k[:-2] if k.endswith("*") else k: v for k, v in grouped.items()}
505
+ for batch_group in self.batch_groups:
506
+ batch_group.name = batch_group.name[:-2] if batch_group.name.endswith(".*") else batch_group.name
507
+ return grouped
508
+
509
+ def create_projects(self):
510
+ def del_ro(func, path, _):
511
+ os.chmod(path, stat.S_IWRITE)
512
+ func(path)
513
+
514
+ if not self.batch_groups:
515
+ self._copy_edb_and_open_project()
516
+ if not self._pedb:
517
+ self._create_project(close_rpc=True)
518
+ else:
519
+ self._create_project(close_rpc=False)
520
+ else:
521
+ batch_count = 0
522
+ if os.path.isdir(self.batch_group_folder):
523
+ shutil.rmtree(self.batch_group_folder)
524
+ for batch_group in self.batch_groups:
525
+ batch_count += 1
526
+ if not self.batch_group_folder:
527
+ self.batch_group_folder = os.path.join(str(Path(self.source_edb_path).parent), "batch_groups")
528
+ if batch_count == 1 and os.path.isdir(self.batch_group_folder):
529
+ os.chdir(os.path.expanduser("~"))
530
+ shutil.rmtree(self.batch_group_folder, onerror=del_ro)
531
+ if batch_group.simulation_setup:
532
+ self.simulation_setup = batch_group.simulation_setup
533
+ self.signal_nets = batch_group.nets
534
+ self.target_edb_path = os.path.join(self.batch_group_folder, batch_group.name + ".aedb")
535
+ self._copy_edb_and_open_project()
536
+ if batch_count == len(self.batch_groups):
537
+ self._create_project(close_rpc=True)
538
+ else:
539
+ self._create_project(close_rpc=False)
540
+
541
+ def _copy_edb_and_open_project(self):
542
+ if not self.source_edb_path:
543
+ raise ValueError("source EDB path is empty.")
544
+ shutil.copytree(self.source_edb_path, self.target_edb_path)
545
+ if not os.path.isdir(self.target_edb_path):
546
+ raise FileNotFoundError(f"Failed to copy EDB to {self.target_edb_path}")
547
+ self._pedb = Edb(edbpath=self.target_edb_path, version=self.ansys_version, grpc=self.grpc)
548
+
549
+ def __get_components_using_signal_nets(self):
550
+ self.components = list(
551
+ set(
552
+ [
553
+ refdes
554
+ for refdes, comp in self._pedb.components.instances.items()
555
+ if comp.type.lower() not in ["resistor", "capacitor", "inductor"]
556
+ and not set(comp.nets).isdisjoint(self.signal_nets)
557
+ ]
558
+ )
559
+ )
560
+
561
+ def _create_project(self, close_rpc: bool = True):
562
+ if not self.target_edb_path:
563
+ raise ValueError("Project path is empty.")
564
+ if not self.signal_nets:
565
+ raise ValueError("No signal nets defined.")
566
+ if not self.reference_net:
567
+ raise ValueError("No reference net defined.")
568
+ # step 1: cutout
569
+ self._pedb.logger.info(f"Creating project {self.target_edb_path}")
570
+ self._pedb.logger.info(f"step 1: cutout")
571
+ clipped_nets = self.power_nets
572
+ clipped_nets.append(self.reference_net)
573
+ self._pedb.cutout(
574
+ signal_list=self.signal_nets,
575
+ reference_list=clipped_nets,
576
+ extent_type=self.extent_type,
577
+ expansion_size=self.cutout_expansion,
578
+ )
579
+ # step 2: create Ports
580
+ self._pedb.logger.info(f"step 2: creating ports")
581
+ if not self.components:
582
+ self._pedb.logger.info("No components provided, searching component instances")
583
+ self.__get_components_using_signal_nets()
584
+ if not self.components:
585
+ raise ValueError("No components found in the design.")
586
+ if self.port_type in ["coaxial", "coax", "coax_port", "coaxial_port"]:
587
+ if self.solder_balls:
588
+ for solder_ball in self.solder_balls:
589
+ comp = solder_ball.ref_des
590
+ if not comp in self.components:
591
+ self._pedb.logger.warning(f"Component {comp} not found in the design, skipping")
592
+ continue
593
+ self._pedb.components.create_port_on_component(
594
+ component=comp,
595
+ net_list=self.signal_nets,
596
+ port_type="coax_port",
597
+ reference_net=self.reference_net,
598
+ solder_balls_height=solder_ball.height,
599
+ solder_balls_size=solder_ball.diameter,
600
+ solder_balls_mid_size=solder_ball.mid_diameter,
601
+ )
602
+ else:
603
+ for component in self.components:
604
+ self._pedb.components.create_port_on_component(
605
+ component=component,
606
+ net_list=self.signal_nets,
607
+ port_type="coax_port",
608
+ reference_net=self.reference_net,
609
+ )
610
+ elif self.port_type in ["circuit_port", "circuit", "circuit_ports"]:
611
+ for component in self.components:
612
+ self._pedb.components.create_port_on_component(
613
+ component=component,
614
+ net_list=self.signal_nets,
615
+ port_type="circuit_port",
616
+ do_pingroup=self.create_pin_group,
617
+ reference_net=self.reference_net,
618
+ )
619
+
620
+ self._pedb.logger.info(f"Ports created: {len(self._pedb.hfss.excitations)}")
621
+ # step 3: create simulation setup
622
+ self._pedb.logger.info(f"step 3: creating simulation setup")
623
+ setup = self._pedb.hfss.add_setup("Setup1")
624
+ setup.adaptive_settings.max_passes = self.simulation_setup.maximum_pass_number
625
+ if not self.grpc:
626
+ setup.adaptive_settings.adaptive_frequency_data_list[
627
+ 0
628
+ ].adaptive_frequency = self.simulation_setup.meshing_frequency
629
+ else:
630
+ setup.settings.mesh_frequency = self.simulation_setup.meshing_frequency
631
+ setup.add_sweep(
632
+ "AutoSweep",
633
+ start_freq=self.simulation_setup.start_frequency,
634
+ stop_freq=self.simulation_setup.stop_frequency,
635
+ step=self.simulation_setup.frequency_step,
636
+ )
637
+ if self.auto_mesh_seeding:
638
+ setup.auto_mesh_operation()
639
+ self._pedb.save()
640
+ self._pedb.close(terminate_rpc_session=close_rpc)
641
+
642
+
643
+ def create_hfss_auto_configuration(
644
+ edb: Optional[Edb] = None,
645
+ ansys_version: Optional[str] = None,
646
+ grpc: Optional[bool] = None,
647
+ source_edb_path: Optional[str] = None,
648
+ target_edb_path: Optional[str] = None,
649
+ signal_nets: Optional[list] = None,
650
+ power_nets: Optional[list] = None,
651
+ reference_net: Optional[str] = None,
652
+ batch_size: Optional[int] = None,
653
+ batch_groups: Optional[list] = None,
654
+ components: Optional[list[str]] = None,
655
+ solder_balls: Optional[list] = None,
656
+ simulation_setup: Optional[SimulationSetup] = None,
657
+ extent_type: Optional[str] = None,
658
+ cutout_expansion: Optional[Union[str, float]] = None,
659
+ auto_mesh_seeding: Optional[bool] = None,
660
+ port_type: Optional[str] = None,
661
+ create_pin_group: Optional[bool] = None,
662
+ ) -> HFSSAutoConfiguration:
663
+ """
664
+ Factory function that creates an HFSSAutoConfiguration instance
665
+ with optional overrides for every public attribute.
666
+
667
+ Parameters
668
+ ----------
669
+ All parameters are optional. When omitted, the class-level defaults
670
+ (defined in HFSSAutoConfiguration.__init__) are kept.
671
+
672
+ Returns
673
+ -------
674
+ HFSSAutoConfiguration
675
+ A fully configured instance ready for further use or inspection.
676
+ """
677
+ cfg = HFSSAutoConfiguration(edb)
678
+
679
+ # Scalar overrides
680
+ for attr, value in (
681
+ ("ansys_version", ansys_version),
682
+ ("grpc", grpc),
683
+ ("source_edb_path", source_edb_path),
684
+ ("target_edb_path", target_edb_path),
685
+ ("batch_size", batch_size),
686
+ ("extent_type", extent_type),
687
+ ("cutout_expansion", cutout_expansion),
688
+ ("auto_mesh_seeding", auto_mesh_seeding),
689
+ ("port_type", port_type),
690
+ ("create_pin_group", create_pin_group),
691
+ ):
692
+ if value is not None:
693
+ setattr(cfg, attr, value)
694
+
695
+ # List / container overrides
696
+ if signal_nets is not None:
697
+ cfg.signal_nets = signal_nets
698
+ if power_nets is not None:
699
+ cfg.power_nets = power_nets
700
+ if reference_net is not None:
701
+ cfg.reference_net = reference_net
702
+ if batch_groups is not None:
703
+ cfg.batch_groups = batch_groups
704
+ if components is not None:
705
+ cfg.components = components
706
+ if solder_balls is not None:
707
+ cfg.solder_balls = solder_balls
708
+ if simulation_setup is not None:
709
+ cfg.simulation_setup = simulation_setup
710
+
711
+ return cfg
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyedb
3
- Version: 0.58.0
3
+ Version: 0.59.0
4
4
  Summary: Higher-Level Pythonic Ansys Electronics Data Base
5
5
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
6
6
  Maintainer-email: PyEDB developers <simon.vandenbrouck@ansys.com>
@@ -1,4 +1,4 @@
1
- pyedb/__init__.py,sha256=7tur0elJHeGehakaik77uV7EtJQP_1mhEoWHLIQGnGI,1327
1
+ pyedb/__init__.py,sha256=JmPrpEANPckijk8EAsOXA4NmGyWhmSeSHJJ_tPPBYEg,1327
2
2
  pyedb/edb_logger.py,sha256=ySZ_cZFJ09s1k3n3ft2q-MjaxH5pUwq18HNdiikTJNM,14497
3
3
  pyedb/exceptions.py,sha256=iStxCzoIOnZH1JWUi1sE9bmCwa8fY1Mg55-aURn9boc,111
4
4
  pyedb/siwave.py,sha256=4ttgMbOBnnCMtyNmU8SbOxkA7Cf0c_lV9cawEtq6V1k,17736
@@ -30,8 +30,8 @@ pyedb/dotnet/clr_module.py,sha256=AEy172pzZMwqbENX1RUTEkEm5viVNGd4Ti9KjSL3UAc,54
30
30
  pyedb/dotnet/edb.py,sha256=UwybReXR8CdR3X4w9tZcPgT4jFVh1oD7MDGBGFt-X2s,194043
31
31
  pyedb/dotnet/database/Variables.py,sha256=YqPk4iReyo-xNmWJ3s26_J35PImA9R0kznl2grzoY1c,78441
32
32
  pyedb/dotnet/database/__init__.py,sha256=nIRLJ8VZLcMAp12zmGsnZ5x2BEEl7q_Kj_KAOXxVjpQ,52
33
- pyedb/dotnet/database/components.py,sha256=HMuBQo4D6bb25-TePCAQ6PVxQ1hp_X-QNOISpWDbNfg,111905
34
- pyedb/dotnet/database/general.py,sha256=3Th6G21uTB_KQ9E8uW6cfB_96WNEe3E2sdi9Zy5OhGw,4669
33
+ pyedb/dotnet/database/components.py,sha256=xG0xJmLy5bPzz4MGDU4_5qYDStIdrDVPaytZbvzBsrw,111533
34
+ pyedb/dotnet/database/general.py,sha256=aKcVyYdQOWsALAH_12RN-l9UKy-ExOoZ88aGikx5iWU,4693
35
35
  pyedb/dotnet/database/hfss.py,sha256=zkoqdK68T-QJEQwmJpEE-ta7qKIYLkLNMx1YCvk2Fvw,69079
36
36
  pyedb/dotnet/database/layout_obj_instance.py,sha256=se6eJ2kfQOAZfAwObCBdr0A7CCD3st4aiPPVJR9eQoA,1407
37
37
  pyedb/dotnet/database/layout_validation.py,sha256=fkOAircLkUhIItoY6V78wVQSgmcfDBFVUMXB2jCN4dI,13921
@@ -97,7 +97,7 @@ pyedb/dotnet/database/sim_setup_data/__init__.py,sha256=8jByHkoaowAYQTCww-zRrTQm
97
97
  pyedb/dotnet/database/sim_setup_data/data/__init__.py,sha256=8jByHkoaowAYQTCww-zRrTQmN061fLz_OHjTLSrzQQY,58
98
98
  pyedb/dotnet/database/sim_setup_data/data/adaptive_frequency_data.py,sha256=tlHI7PUUoseNnJAtihpjb1PwXYNr-4ztAAnunlLLWVQ,2463
99
99
  pyedb/dotnet/database/sim_setup_data/data/mesh_operation.py,sha256=WqDQjs1yA8MDX0PtybjDEelvnn5B9L2xIts0BH4Y9dw,8775
100
- pyedb/dotnet/database/sim_setup_data/data/settings.py,sha256=627XwAFnQw3x_5HsJzYDAqW9Y-xpuK-mntjqg_aaK08,30082
100
+ pyedb/dotnet/database/sim_setup_data/data/settings.py,sha256=IrFy5Y8FLsBLnw-YRfk-OMw8JqNn8OgXBwcLGG3U4xo,30076
101
101
  pyedb/dotnet/database/sim_setup_data/data/sim_setup_info.py,sha256=hN2TeXa8dbtOmEtwobhKuwomJXYs8cSJum3HQofuW3Y,4479
102
102
  pyedb/dotnet/database/sim_setup_data/data/simulation_settings.py,sha256=ag-nl1gwKlNJOb3y7fBMSoSEwbUG_rwLzM25jLp8ado,10727
103
103
  pyedb/dotnet/database/sim_setup_data/data/siw_dc_ir_settings.py,sha256=FnvDY1oxpnPo0EYMVXT7yfW-e-W3_NOGnvUvQYta4Ls,8627
@@ -126,8 +126,8 @@ pyedb/generic/plot.py,sha256=7xrCl0OlTXM8uhS6PoyB01OaEXsMmC6xfP3TiV5zguo,4228
126
126
  pyedb/generic/process.py,sha256=KviVNw9KP-iB5QIY6WMZ0sBErcQbLfxsXJjlCwHo9Yc,12163
127
127
  pyedb/generic/settings.py,sha256=nTVTEvZJOrMSXpIYmmHKKCiVUvECL_aiHcQ3BSX2HMw,12122
128
128
  pyedb/grpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
- pyedb/grpc/edb.py,sha256=Sqnfjq_yhNqubBKx0Sl1KpxtKA5qJRh7ey9q2Y-D1U8,152775
130
- pyedb/grpc/edb_init.py,sha256=v2rCxQ4prUQBRMKZgC01OmFOSGvASsuRikHeROZ0SN4,15741
129
+ pyedb/grpc/edb.py,sha256=bNidAue3vL4UpvQK89-fx1-KGio3jeHEIEBBjjGY7IU,152754
130
+ pyedb/grpc/edb_init.py,sha256=dXepudKz0R65cjIG2qchap-nYCEYW2gYtfwn3P58YUI,15447
131
131
  pyedb/grpc/rpc_session.py,sha256=espO-OFMeGi4Gms4DZAhnef7LaSvzpLmRI0kQu3ul4c,7157
132
132
  pyedb/grpc/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  pyedb/grpc/database/_typing.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -299,7 +299,8 @@ pyedb/siwave_core/icepak.py,sha256=WnZ-t8mik7LDY06V8hZFV-TxRZJQWK7bu_8Ichx-oBs,5
299
299
  pyedb/siwave_core/product_properties.py,sha256=m7HIMeYKJZqfzWbJklEOKqi3KJHwhj7W0SRbkRCng_c,5660
300
300
  pyedb/siwave_core/cpa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
301
301
  pyedb/siwave_core/cpa/simulation_setup_data_model.py,sha256=hQsDCvfSDGv3kdDdkTjJYlQqrP1mT4_-_sR0_iQFxi8,5577
302
- pyedb-0.58.0.dist-info/licenses/LICENSE,sha256=qQWivZ12ETN5l3QxvTARY-QI5eoRRlyHdwLlAj0Bg5I,1089
303
- pyedb-0.58.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
304
- pyedb-0.58.0.dist-info/METADATA,sha256=tvkDZ6KHeit_Le_UtZjDKEhqfnqKAxymIsboMGiHDfU,8653
305
- pyedb-0.58.0.dist-info/RECORD,,
302
+ pyedb/workflows/sipi/hfss_auto_configuration.py,sha256=FIlsuYyt5HicdRRs6CxJsWWZWSFfDdeMWlxD9MwWQKo,28307
303
+ pyedb-0.59.0.dist-info/licenses/LICENSE,sha256=qQWivZ12ETN5l3QxvTARY-QI5eoRRlyHdwLlAj0Bg5I,1089
304
+ pyedb-0.59.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
305
+ pyedb-0.59.0.dist-info/METADATA,sha256=doDDMsPzXJUgBs0UHs12cLAtq_9GtUS3-oanIbLBTkY,8653
306
+ pyedb-0.59.0.dist-info/RECORD,,
File without changes