pyedb 0.51.2__py3-none-any.whl → 0.53.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 (38) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_common.py +12 -15
  3. pyedb/configuration/cfg_data.py +2 -2
  4. pyedb/configuration/cfg_modeler.py +163 -234
  5. pyedb/configuration/cfg_stackup.py +62 -249
  6. pyedb/configuration/configuration.py +356 -409
  7. pyedb/dotnet/database/components.py +9 -3
  8. pyedb/dotnet/database/dotnet/database.py +4 -0
  9. pyedb/dotnet/database/edb_data/layer_data.py +3 -1
  10. pyedb/dotnet/database/edb_data/padstacks_data.py +8 -2
  11. pyedb/dotnet/database/layout_validation.py +3 -13
  12. pyedb/dotnet/database/siwave.py +14 -0
  13. pyedb/dotnet/database/stackup.py +8 -61
  14. pyedb/dotnet/database/utilities/simulation_setup.py +1 -1
  15. pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +894 -0
  16. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +15 -0
  17. pyedb/dotnet/edb.py +50 -3
  18. pyedb/generic/design_types.py +29 -0
  19. pyedb/generic/grpc_warnings.py +5 -0
  20. pyedb/grpc/database/__init__.py +0 -1
  21. pyedb/grpc/database/components.py +102 -81
  22. pyedb/grpc/database/control_file.py +240 -193
  23. pyedb/grpc/database/definition/materials.py +7 -7
  24. pyedb/grpc/database/definitions.py +7 -5
  25. pyedb/grpc/database/hierarchy/pin_pair_model.py +1 -1
  26. pyedb/grpc/database/modeler.py +105 -77
  27. pyedb/grpc/database/net/differential_pair.py +2 -1
  28. pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +961 -0
  29. pyedb/grpc/database/siwave.py +14 -0
  30. pyedb/grpc/database/source_excitations.py +10 -10
  31. pyedb/grpc/edb.py +156 -180
  32. pyedb/grpc/edb_init.py +4 -2
  33. pyedb/siwave_core/cpa/simulation_setup_data_model.py +132 -0
  34. pyedb/siwave_core/product_properties.py +198 -0
  35. {pyedb-0.51.2.dist-info → pyedb-0.53.0.dist-info}/METADATA +16 -14
  36. {pyedb-0.51.2.dist-info → pyedb-0.53.0.dist-info}/RECORD +38 -33
  37. {pyedb-0.51.2.dist-info → pyedb-0.53.0.dist-info}/WHEEL +1 -1
  38. {pyedb-0.51.2.dist-info → pyedb-0.53.0.dist-info/licenses}/LICENSE +0 -0
@@ -19,362 +19,35 @@
19
19
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
-
23
22
  from datetime import datetime
24
23
  import json
25
24
  import os
26
25
  from pathlib import Path
26
+ import warnings
27
27
 
28
28
  import toml
29
29
 
30
+ from pyedb import Edb
30
31
  from pyedb.configuration.cfg_data import CfgData
31
- from pyedb.dotnet.database.definition.package_def import PackageDef
32
32
 
33
33
 
34
34
  class Configuration:
35
35
  """Enables export and import of a JSON configuration file that can be applied to a new or existing design."""
36
36
 
37
- class Grpc:
38
- def __init__(self, parent):
39
- self.parent = parent
40
- self._pedb = parent._pedb
41
-
42
- def configuration_stackup(self, kwargs):
43
- if kwargs.get("fix_padstack_def"):
44
- from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
45
-
46
- pedb_defs = self._pedb.padstacks.definitions
47
- temp = []
48
- for _, pdef in pedb_defs.items():
49
- cfg_def = CfgPadstackDefinition(self._pedb, pdef)
50
- cfg_def.api.retrieve_parameters_from_edb()
51
- temp.append(cfg_def)
52
- self.parent.cfg_data.stackup.apply()
53
- for cfg_pdef in temp:
54
- cfg_pdef.api.set_parameters_to_edb()
55
- else:
56
- temp_pdef_data = {}
57
- from ansys.edb.core.definition.padstack_def_data import (
58
- PadType as GrpcPadType,
59
- )
60
-
61
- for pdef_name, pdef in self._pedb.padstacks.definitions.items():
62
- for layer in pdef.data.layer_names:
63
- result = pdef.data.get_pad_parameters(layer, GrpcPadType.REGULAR_PAD)
64
- if len(result) == 4:
65
- # polygon based
66
- temp_pdef_data[pdef_name] = pdef.data
67
- break
68
- self.parent.cfg_data.stackup.apply()
69
- for pdef_name, pdef_data in temp_pdef_data.items():
70
- pdef = self._pedb.padstacks.definitions[pdef_name]
71
- pdef._padstack_def_data = pdef_data
72
-
73
- def _load_stackup(self):
74
- """Imports stackup information from json."""
75
- data = self.data["stackup"]
76
- materials = data.get("materials")
77
-
78
- if materials:
79
- edb_materials = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
80
- for mat in materials:
81
- name = mat["name"].lower()
82
- if name in edb_materials:
83
- self._pedb.materials.delete_material(edb_materials[name])
84
- for mat in materials:
85
- self._pedb.materials.add_material(**mat)
86
-
87
- layers = data.get("layers")
88
-
89
- if layers:
90
- input_signal_layers = [i for i in layers if i["type"].lower() == "signal"]
91
- if not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
92
- self._pedb.logger.error("Input signal layer count do not match.")
93
- return False
94
-
95
- removal_list = []
96
- lc_signal_layers = []
97
- for name, obj in self._pedb.stackup.all_layers.items():
98
- if obj.type == "dielectric":
99
- removal_list.append(name)
100
- elif obj.type == "signal":
101
- lc_signal_layers.append(obj.id)
102
- for l in removal_list:
103
- self._pedb.stackup.remove_layer(l)
104
-
105
- # update all signal layers
106
- id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
107
- signal_idx = 0
108
- for l in layers:
109
- if l["type"] == "signal":
110
- layer_id = lc_signal_layers[signal_idx]
111
- layer_name = id_name[layer_id]
112
- self._pedb.stackup.layers[layer_name].update(**l)
113
- signal_idx = signal_idx + 1
114
-
115
- # add all dielectric layers. Dielectric layers must be added last. Otherwise,
116
- # dielectric layer will occupy signal and document layer id.
117
- prev_layer_clone = None
118
- l = layers.pop(0)
119
- if l["type"] == "signal":
120
- prev_layer_clone = self._pedb.stackup.layers[l["name"]]
121
- else:
122
- prev_layer_clone = self._pedb.stackup.add_layer_top(**l)
123
- for idx, l in enumerate(layers):
124
- if l["type"] == "dielectric":
125
- prev_layer_clone = self._pedb.stackup.add_layer_below(
126
- base_layer_name=prev_layer_clone.name, **l
127
- )
128
- elif l["type"] == "signal":
129
- prev_layer_clone = self._pedb.stackup.layers[l["name"]]
130
-
131
- def _load_package_def(self):
132
- """Imports package definition information from JSON."""
133
- comps = self._pedb.components.instances
134
- for pkgd in self.parent.data["package_definitions"]:
135
- name = pkgd["name"]
136
- if name in self._pedb.definitions.package:
137
- self._pedb.definitions.package[name].delete()
138
- extent_bounding_box = pkgd.get("extent_bounding_box", None)
139
- if extent_bounding_box:
140
- package_def = PackageDef(self._pedb, name=name, extent_bounding_box=extent_bounding_box)
141
- else:
142
- package_def = PackageDef(self._pedb, name=name, component_part_name=pkgd["component_definition"])
143
- package_def.maximum_power = pkgd["maximum_power"]
144
- package_def.therm_cond = pkgd["therm_cond"]
145
- package_def.theta_jb = pkgd["theta_jb"]
146
- package_def.theta_jc = pkgd["theta_jc"]
147
- package_def.height = pkgd["height"]
148
-
149
- heatsink = pkgd.get("heatsink", None)
150
- if heatsink:
151
- package_def.set_heatsink(
152
- heatsink["fin_base_height"],
153
- heatsink["fin_height"],
154
- heatsink["fin_orientation"],
155
- heatsink["fin_spacing"],
156
- heatsink["fin_thickness"],
157
- )
158
-
159
- comp_def_name = pkgd["component_definition"]
160
- comp_def = self._pedb.definitions.component[comp_def_name]
161
-
162
- comp_list = dict()
163
- if pkgd["apply_to_all"]:
164
- comp_list.update(
165
- {
166
- refdes: comp
167
- for refdes, comp in comp_def.components.items()
168
- if refdes not in pkgd["components"]
169
- }
170
- )
171
- else:
172
- comp_list.update(
173
- {refdes: comp for refdes, comp in comp_def.components.items() if refdes in pkgd["components"]}
174
- )
175
- for _, i in comp_list.items():
176
- i.package_def = name
177
-
178
- def get_data_from_db(self, **kwargs):
179
- """Get configuration data from layout.
180
-
181
- Parameters
182
- ----------
183
- stackup
184
-
185
- Returns
186
- -------
187
-
188
- """
189
- self._pedb.logger.info("Getting data from layout database.")
190
- data = {}
191
- if kwargs.get("general", False):
192
- data["general"] = self.parent.cfg_data.general.get_data_from_db()
193
- if kwargs.get("stackup", False):
194
- data["stackup"] = self.parent.cfg_data.stackup.get_data_from_db()
195
- if kwargs.get("package_definitions", False):
196
- data["package_definitions"] = self.parent.cfg_data.package_definitions.get_data_from_db()
197
- if kwargs.get("setups", False):
198
- setups = self.parent.cfg_data.setups
199
- setups.retrieve_parameters_from_edb()
200
- data["setups"] = setups.to_dict()
201
- if kwargs.get("sources", False):
202
- data["sources"] = self.parent.cfg_data.sources.get_data_from_db()
203
- if kwargs.get("ports", False):
204
- data["ports"] = self.parent.cfg_data.ports.get_data_from_db()
205
- if kwargs.get("components", False) or kwargs.get("s_parameters", False):
206
- self.parent.cfg_data.components.retrieve_parameters_from_edb()
207
- components = []
208
- for i in self.parent.cfg_data.components.components:
209
- if i.type == "io":
210
- components.append(i.get_attributes())
211
- components.append(i.get_attributes())
212
-
213
- if kwargs.get("components", False):
214
- data["components"] = components
215
- elif kwargs.get("s_parameters", False):
216
- data["s_parameters"] = self.parent.cfg_data.s_parameters.get_data_from_db(components)
217
- if kwargs.get("nets", False):
218
- data["nets"] = self.parent.cfg_data.nets.get_data_from_db()
219
- if kwargs.get("pin_groups", False):
220
- data["pin_groups"] = self.parent.cfg_data.pin_groups.get_data_from_db()
221
- if kwargs.get("operations", False):
222
- data["operations"] = self.parent.cfg_data.operations.get_data_from_db()
223
- if kwargs.get("padstacks", False):
224
- self.parent.cfg_data.padstacks.retrieve_parameters_from_edb()
225
- definitions = []
226
- for i in self.parent.cfg_data.padstacks.definitions:
227
- definitions.append(i.get_attributes())
228
- instances = []
229
- for i in self.parent.cfg_data.padstacks.instances:
230
- instances.append(i.get_attributes())
231
- data["padstacks"] = dict()
232
- data["padstacks"]["definitions"] = definitions
233
- data["padstacks"]["instances"] = instances
234
-
235
- if kwargs.get("boundaries", False):
236
- data["boundaries"] = self.parent.cfg_data.boundaries.get_data_from_db()
237
-
238
- return data
239
-
240
- def export(
241
- self,
242
- file_path,
243
- stackup=True,
244
- package_definitions=False,
245
- setups=True,
246
- sources=True,
247
- ports=True,
248
- nets=True,
249
- pin_groups=True,
250
- operations=True,
251
- components=True,
252
- boundaries=True,
253
- s_parameters=True,
254
- padstacks=True,
255
- general=True,
256
- ):
257
- """Export the configuration data from layout to a file.
258
-
259
- Parameters
260
- ----------
261
- file_path : str, Path
262
- File path to export the configuration data.
263
- stackup : bool
264
- Whether to export stackup or not.
265
- package_definitions : bool
266
- Whether to export package definitions or not.
267
- setups : bool
268
- Whether to export setups or not.
269
- sources : bool
270
- Whether to export sources or not.
271
- ports : bool
272
- Whether to export ports or not.
273
- nets : bool
274
- Whether to export nets.
275
- pin_groups : bool
276
- Whether to export pin groups.
277
- operations : bool
278
- Whether to export operations.
279
- components : bool
280
- Whether to export component.
281
- boundaries : bool
282
- Whether to export boundaries.
283
- s_parameters : bool
284
- Whether to export s_parameters.
285
- padstacks : bool
286
- Whether to export padstacks.
287
- general : bool
288
- Whether to export general information.
289
- Returns
290
- -------
291
- bool
292
- """
293
- data = self.get_data_from_db(
294
- stackup=stackup,
295
- package_definitions=package_definitions,
296
- setups=setups,
297
- sources=sources,
298
- ports=ports,
299
- nets=nets,
300
- pin_groups=pin_groups,
301
- operations=operations,
302
- components=components,
303
- boundaries=boundaries,
304
- s_parameters=s_parameters,
305
- padstacks=padstacks,
306
- general=general,
307
- )
308
-
309
- file_path = file_path if isinstance(file_path, Path) else Path(file_path)
310
- file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
311
-
312
- for comp in data["components"]:
313
- for key, value in comp.items():
314
- try:
315
- json.dumps(value)
316
- print(f"Key '{key}' is serializable.")
317
- except TypeError as e:
318
- print(f"Key '{key}' failed: {e}")
319
-
320
- with open(file_path, "w") as f:
321
- if file_path.suffix == ".json":
322
- json.dump(data, f, ensure_ascii=False, indent=4)
323
- else:
324
- toml.dump(data, f)
325
- return True if os.path.isfile(file_path) else False
326
-
327
- class DotNet(Grpc):
328
- def __init__(self, parent):
329
- super().__init__(parent)
330
-
331
- def configuration_stackup(self, kwargs):
332
- if kwargs.get("fix_padstack_def"):
333
- from pyedb.configuration.cfg_padstacks import CfgPadstackDefinition
334
-
335
- pedb_defs = self._pedb.padstacks.definitions
336
- temp = []
337
- for _, pdef in pedb_defs.items():
338
- cfg_def = CfgPadstackDefinition(self._pedb, pdef)
339
- cfg_def.api.retrieve_parameters_from_edb()
340
- temp.append(cfg_def)
341
- self.parent.cfg_data.stackup.apply()
342
- for cfg_pdef in temp:
343
- cfg_pdef.api.set_parameters_to_edb()
344
- else:
345
- temp_pdef_data = {}
346
- for pdef_name, pdef in self._pedb.padstacks.definitions.items():
347
- pdef_data = pdef._padstack_def_data
348
- for lyr_name in list(pdef_data.GetLayerNames()):
349
- result = pdef_data.GetPadParametersValue(
350
- lyr_name, self._pedb._edb.Definition.PadType.RegularPad
351
- )
352
- flag, pad_shape, params, offset_x, offset_y, rotation = result
353
- if flag is False:
354
- result = pdef_data.GetPolygonalPadParameters(
355
- lyr_name, self._pedb._edb.Definition.PadType.RegularPad
356
- )
357
- flag, polygon_data, offset_x, offset_y, rotation = result
358
- if flag:
359
- temp_pdef_data[pdef_name] = pdef_data
360
- break
361
- self.parent.cfg_data.stackup.apply()
362
- for pdef_name, pdef_data in temp_pdef_data.items():
363
- pdef = self._pedb.padstacks.definitions[pdef_name]
364
- pdef._padstack_def_data = pdef_data
365
-
366
- def __init__(self, pedb):
37
+ def __init__(self, pedb: Edb):
367
38
  self._pedb = pedb
368
- if self._pedb.grpc:
369
- self.api = self.Grpc(self)
370
- else:
371
- self.api = self.DotNet(self)
39
+
372
40
  self._components = self._pedb.components.instances
373
41
  self.data = {}
374
42
  self._s_parameter_library = ""
375
43
  self._spice_model_library = ""
376
44
  self.cfg_data = CfgData(self._pedb)
377
45
 
46
+ def __apply_with_logging(self, label: str, func):
47
+ start = datetime.now()
48
+ func()
49
+ self._pedb.logger.info(f"{label} finished. Time lapse {datetime.now() - start}")
50
+
378
51
  def load(self, config_file, append=True, apply_file=False, output_file=None, open_at_the_end=True):
379
52
  """Import configuration settings from a configure file.
380
53
 
@@ -439,97 +112,295 @@ class Configuration:
439
112
 
440
113
  def run(self, **kwargs):
441
114
  """Apply configuration settings to the current design"""
115
+ if kwargs.get("fix_padstack_def"):
116
+ warnings.warn("fix_padstack_def is deprecated.", DeprecationWarning)
442
117
 
443
- if self.cfg_data.variables:
444
- self.cfg_data.variables.apply()
118
+ self.apply_variables()
445
119
 
446
120
  if self.cfg_data.general:
447
121
  self.cfg_data.general.apply()
448
122
 
449
123
  # Configure boundary settings
450
- now = datetime.now()
451
124
  if self.cfg_data.boundaries:
452
- self.cfg_data.boundaries.apply()
453
- self._pedb.logger.info(f"Updating boundaries finished. Time lapse {datetime.now() - now}")
454
- now = datetime.now()
125
+ self.__apply_with_logging("Updating boundaries", self.cfg_data.boundaries.apply)
455
126
 
456
- # Configure nets
457
127
  if self.cfg_data.nets:
458
- self.cfg_data.nets.apply()
459
- self._pedb.logger.info(f"Updating nets finished. Time lapse {datetime.now() - now}")
460
- now = datetime.now()
461
-
462
- # Configure components
463
- self.cfg_data.components.apply()
464
- self._pedb.logger.info(f"Updating components finished. Time lapse {datetime.now() - now}")
465
- now = datetime.now()
466
-
467
- # Configure pin groups
468
- self.cfg_data.pin_groups.apply()
469
- self._pedb.logger.info(f"Creating pin groups finished. Time lapse {datetime.now() - now}")
470
- now = datetime.now()
471
-
472
- # Configure sources
473
- self.cfg_data.sources.apply()
474
- self._pedb.logger.info(f"Placing sources finished. Time lapse {datetime.now() - now}")
475
- now = datetime.now()
476
-
477
- # Configure setup
478
- self.cfg_data.setups.apply()
479
- self._pedb.logger.info(f"Creating setups finished. Time lapse {datetime.now() - now}")
480
- now = datetime.now()
481
-
482
- # Configure stackup
483
- self.api.configuration_stackup(kwargs)
484
- self._pedb.logger.info(f"Updating stackup finished. Time lapse {datetime.now() - now}")
485
- now = datetime.now()
486
-
487
- # Configure padstacks
128
+ self.__apply_with_logging("Updating nets", self.cfg_data.nets.apply)
129
+
130
+ self.__apply_with_logging("Updating components", self.cfg_data.components.apply)
131
+ self.__apply_with_logging("Creating pin groups", self.cfg_data.pin_groups.apply)
132
+ self.__apply_with_logging("Placing sources", self.cfg_data.sources.apply)
133
+ self.__apply_with_logging("Creating setups", self.cfg_data.setups.apply)
134
+
135
+ self.__apply_with_logging("Applying materials", self.apply_materials)
136
+ self.__apply_with_logging("Updating stackup", self.apply_stackup)
137
+
488
138
  if self.cfg_data.padstacks:
489
- self.cfg_data.padstacks.apply()
490
- self._pedb.logger.info(f"Applying padstacks finished. Time lapse {datetime.now() - now}")
491
- now = datetime.now()
139
+ self.__apply_with_logging("Applying padstacks", self.cfg_data.padstacks.apply)
492
140
 
493
- # Configure S-parameter
494
- self.cfg_data.s_parameters.apply()
495
- self._pedb.logger.info(f"Applying S-parameters finished. Time lapse {datetime.now() - now}")
496
- now = datetime.now()
141
+ self.__apply_with_logging("Applying S-parameters", self.cfg_data.s_parameters.apply)
497
142
 
498
- # Configure SPICE models
499
143
  for spice_model in self.cfg_data.spice_models:
500
- spice_model.apply()
501
- self._pedb.logger.info(f"Assigning Spice models finished. Time lapse {datetime.now() - now}")
502
- now = datetime.now()
144
+ self.__apply_with_logging(f"Assigning Spice model {spice_model}", spice_model.apply)
503
145
 
504
- # Configure package definitions
505
- self.cfg_data.package_definitions.apply()
506
- self._pedb.logger.info(f"Applying package definitions finished. Time lapse {datetime.now() - now}")
507
- now = datetime.now()
146
+ self.__apply_with_logging("Applying package definitions", self.cfg_data.package_definitions.apply)
147
+ self.__apply_with_logging("Applying modeler", self.apply_modeler)
148
+ self.__apply_with_logging("Placing ports", self.cfg_data.ports.apply)
149
+ self.__apply_with_logging("Placing probes", self.cfg_data.probes.apply)
150
+ self.__apply_with_logging("Applying operations", self.cfg_data.operations.apply)
508
151
 
509
- # Modeler
510
- self.cfg_data.modeler.apply()
152
+ return True
511
153
 
512
- # Configure ports
513
- self.cfg_data.ports.apply()
514
- self._pedb.logger.info(f"Placing ports finished. Time lapse {datetime.now() - now}")
515
- now = datetime.now()
154
+ def apply_modeler(self):
155
+ modeler = self.cfg_data.modeler
156
+ if modeler.traces:
157
+ for t in modeler.traces:
158
+ if t.path:
159
+ obj = self._pedb.modeler.create_trace(
160
+ path_list=t.path,
161
+ layer_name=t.layer,
162
+ net_name=t.net_name,
163
+ width=t.width,
164
+ start_cap_style=t.start_cap_style,
165
+ end_cap_style=t.end_cap_style,
166
+ corner_style=t.corner_style,
167
+ )
168
+ obj.aedt_name = t.name
169
+ else:
170
+ obj = self._pedb.modeler.create_trace(
171
+ path_list=[t.incremental_path[0]],
172
+ layer_name=t.layer,
173
+ net_name=t.net_name,
174
+ width=t.width,
175
+ start_cap_style=t.start_cap_style,
176
+ end_cap_style=t.end_cap_style,
177
+ corner_style=t.corner_style,
178
+ )
179
+ obj.aedt_name = t.name
180
+ for x, y in t.incremental_path[1:]:
181
+ obj.add_point(x, y, True)
182
+
183
+ if modeler.padstack_defs:
184
+ for p in modeler.padstack_defs:
185
+ pdata = self._pedb._edb.Definition.PadstackDefData.Create()
186
+ pdef = self._pedb._edb.Definition.PadstackDef.Create(self._pedb.active_db, p.name)
187
+ pdef.SetData(pdata)
188
+ pdef = self._pedb.pedb_class.database.edb_data.padstacks_data.EDBPadstack(pdef, self._pedb.padstacks)
189
+ p.pyedb_obj = pdef
190
+ p.api.set_parameters_to_edb()
191
+
192
+ if modeler.padstack_instances:
193
+ for p in modeler.padstack_instances:
194
+ p_inst = self._pedb.padstacks.place(
195
+ via_name=p.name,
196
+ net_name=p.net_name,
197
+ position=p.position,
198
+ definition_name=p.definition,
199
+ rotation=p.rotation if p.rotation is not None else 0,
200
+ )
201
+ p.pyedb_obj = p_inst
202
+ p.api.set_parameters_to_edb()
203
+
204
+ if modeler.planes:
205
+ for p in modeler.planes:
206
+ if p.type == "rectangle":
207
+ obj = self._pedb.modeler.create_rectangle(
208
+ layer_name=p.layer,
209
+ net_name=p.net_name,
210
+ lower_left_point=p.lower_left_point,
211
+ upper_right_point=p.upper_right_point,
212
+ corner_radius=p.corner_radius,
213
+ rotation=p.rotation,
214
+ )
215
+ obj.aedt_name = p.name
216
+ elif p.type == "polygon":
217
+ obj = self._pedb.modeler.create_polygon(
218
+ main_shape=p.points, layer_name=p.layer, net_name=p.net_name
219
+ )
220
+ obj.aedt_name = p.name
221
+ elif p.type == "circle":
222
+ obj = self._pedb.modeler.create_circle(
223
+ layer_name=p.layer,
224
+ net_name=p.net_name,
225
+ x=p.position[0],
226
+ y=p.position[1],
227
+ radius=p.radius,
228
+ )
229
+ obj.aedt_name = p.name
230
+ else:
231
+ raise RuntimeError(f"Plane type {p.type} not supported")
232
+
233
+ for v in p.voids:
234
+ for i in self._pedb.layout.primitives:
235
+ if i.aedt_name == v:
236
+ self._pedb.modeler.add_void(obj, i)
237
+
238
+ if modeler.components:
239
+ for c in modeler.components:
240
+ obj = self._pedb.components.create(
241
+ c.pins,
242
+ component_name=c.reference_designator,
243
+ placement_layer=c.placement_layer,
244
+ component_part_name=c.definition,
245
+ )
246
+ c.pyedb_obj = obj
247
+ c.api.set_parameters_to_edb()
248
+
249
+ primitives = self._pedb.layout.find_primitive(**modeler.primitives_to_delete)
250
+ for i in primitives:
251
+ i.delete()
252
+
253
+ def apply_variables(self):
254
+ """Set variables into database."""
255
+ inst = self.cfg_data.variables
256
+ for i in inst.variables:
257
+ if i.name.startswith("$"):
258
+ self._pedb.add_project_variable(i.name, i.value, i.description)
259
+ else:
260
+ self._pedb.add_design_variable(i.name, i.value, description=i.description)
516
261
 
517
- # Configure probes
518
- self.cfg_data.probes.apply()
519
- self._pedb.logger.info(f"Placing probes finished. Time lapse {datetime.now() - now}")
262
+ def get_variables(self):
263
+ """Retrieve variables from database."""
264
+ for name, obj in self._pedb.design_variables.items():
265
+ self.cfg_data.variables.add_variable(name, obj.value_string, obj.description)
266
+ for name, obj in self._pedb.project_variables.items():
267
+ self.cfg_data.variables.add_variable(name, obj.value_string, obj.description)
520
268
 
521
- # Configure operations
522
- self.cfg_data.operations.apply()
269
+ def apply_materials(self):
270
+ """Apply material settings to the current design"""
271
+ cfg_stackup = self.cfg_data.stackup
272
+ if len(cfg_stackup.materials):
273
+ materials_in_db = {i.lower(): i for i, _ in self._pedb.materials.materials.items()}
274
+ for mat_in_cfg in cfg_stackup.materials:
275
+ if mat_in_cfg.name.lower() in materials_in_db:
276
+ self._pedb.materials.delete_material(materials_in_db[mat_in_cfg.name.lower()])
523
277
 
524
- return True
278
+ attrs = mat_in_cfg.model_dump(exclude_none=True)
279
+ mat = self._pedb.materials.add_material(**attrs)
280
+
281
+ for i in attrs.get("thermal_modifiers", []):
282
+ mat.set_thermal_modifier(**i.to_dict())
283
+
284
+ def get_materials(self):
285
+ """Retrieve materials from the current design.
286
+
287
+ Parameters
288
+ ----------
289
+ append: bool, optional
290
+ If `True`, append materials to the current material list.
291
+ """
525
292
 
526
- def _load_stackup(self):
527
- """Imports stackup information from json."""
528
- self.api._load_stackup()
293
+ self.cfg_data.stackup.materials = []
294
+ for name, mat in self._pedb.materials.materials.items():
295
+ self.cfg_data.stackup.add_material(**mat.to_dict())
296
+
297
+ def apply_stackup(self):
298
+ layers = self.cfg_data.stackup.layers
299
+ input_signal_layers = [i for i in layers if i.type.lower() == "signal"]
300
+ if len(input_signal_layers) == 0:
301
+ return
302
+ else: # Create materials with default properties used in stackup but not defined
303
+ materials = [m.name for m in self.cfg_data.stackup.materials]
304
+ for i in self.cfg_data.stackup.layers:
305
+ if i.type == "signal":
306
+ if i.material not in materials:
307
+ self.cfg_data.stackup.add_material(
308
+ name=i.material, **self._pedb.materials.default_conductor_property_values
309
+ )
529
310
 
530
- def _load_package_def(self):
531
- """Imports package definition information from JSON."""
532
- self.api._load_package_def()
311
+ if i.fill_material not in materials:
312
+ self.cfg_data.stackup.add_material(
313
+ name=i.material, **self._pedb.materials.default_dielectric_property_values
314
+ )
315
+
316
+ elif i.type == "dielectric":
317
+ if i.material not in materials:
318
+ self.cfg_data.stackup.add_material(
319
+ name=i.material, **self._pedb.materials.default_dielectric_property_values
320
+ )
321
+
322
+ if len(self._pedb.stackup.signal_layers) == 0:
323
+ self.__create_stackup()
324
+ elif not len(input_signal_layers) == len(self._pedb.stackup.signal_layers):
325
+ raise Exception(f"Input signal layer count do not match.")
326
+ else:
327
+ self.__update_stackup()
328
+
329
+ def __create_stackup(self):
330
+ layers_ = list()
331
+ layers_.extend(self.cfg_data.stackup.layers)
332
+ for l_attrs in layers_:
333
+ attrs = l_attrs.model_dump(exclude_none=True)
334
+ self._pedb.stackup.add_layer_bottom(**attrs)
335
+
336
+ def __update_stackup(self):
337
+ """Apply layer settings to the current design"""
338
+
339
+ # After import stackup, padstacks lose their definitions. They need to be fixed after loading stackup
340
+ # step 1, archive padstack definitions
341
+ temp_pdef_data = {}
342
+ for pdef_name, pdef in self._pedb.padstacks.definitions.items():
343
+ pdef_edb_object = pdef._padstack_def_data
344
+ temp_pdef_data[pdef_name] = pdef_edb_object
345
+ # step 2, archive padstack instance layer map
346
+ temp_p_inst_layer_map = {}
347
+ for p_inst in self._pedb.layout.padstack_instances:
348
+ temp_p_inst_layer_map[p_inst.id] = p_inst._edb_object.GetLayerMap()
349
+
350
+ # ----------------------------------------------------------------------
351
+ # Apply stackup
352
+ layers = list()
353
+ layers.extend(self.cfg_data.stackup.layers)
354
+
355
+ removal_list = []
356
+ lc_signal_layers = []
357
+ for name, obj in self._pedb.stackup.all_layers.items():
358
+ if obj.type == "dielectric":
359
+ removal_list.append(name)
360
+ elif obj.type == "signal":
361
+ lc_signal_layers.append(obj.id)
362
+ for l in removal_list:
363
+ self._pedb.stackup.remove_layer(l)
364
+
365
+ # update all signal layers
366
+ id_name = {i[0]: i[1] for i in self._pedb.stackup.layers_by_id}
367
+ signal_idx = 0
368
+ for l in layers:
369
+ if l.type == "signal":
370
+ layer_id = lc_signal_layers[signal_idx]
371
+ layer_name = id_name[layer_id]
372
+ attrs = l.model_dump(exclude_none=True)
373
+ self._pedb.stackup.layers[layer_name].update(**attrs)
374
+ signal_idx = signal_idx + 1
375
+
376
+ # add all dielectric layers. Dielectric layers must be added last. Otherwise,
377
+ # dielectric layer will occupy signal and document layer id.
378
+ l = layers.pop(0)
379
+ if l.type == "signal":
380
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
381
+ else:
382
+ attrs = l.model_dump(exclude_none=True)
383
+ prev_layer_clone = self._pedb.stackup.add_layer_top(**attrs)
384
+ for idx, l in enumerate(layers):
385
+ if l.type == "dielectric":
386
+ attrs = l.model_dump(exclude_none=True)
387
+ prev_layer_clone = self._pedb.stackup.add_layer_below(base_layer_name=prev_layer_clone.name, **attrs)
388
+ elif l.type == "signal":
389
+ prev_layer_clone = self._pedb.stackup.layers[l.name]
390
+
391
+ # ----------------------------------------------------------------------
392
+ # restore padstack definitions
393
+ for pdef_name, pdef_data in temp_pdef_data.items():
394
+ pdef = self._pedb.padstacks.definitions[pdef_name]
395
+ pdef._padstack_def_data = pdef_data
396
+ # restore padstack instance layer map
397
+ for p_inst in self._pedb.layout.padstack_instances:
398
+ p_inst._edb_object.SetLayerMap(temp_p_inst_layer_map[p_inst.id])
399
+
400
+ def get_stackup(self):
401
+ self.cfg_data.stackup.layers = []
402
+ for name, obj in self._pedb.stackup.all_layers.items():
403
+ self.cfg_data.stackup.add_layer_at_bottom(**obj.properties)
533
404
 
534
405
  def get_data_from_db(self, **kwargs):
535
406
  """Get configuration data from layout.
@@ -542,7 +413,62 @@ class Configuration:
542
413
  -------
543
414
 
544
415
  """
545
- return self.api.get_data_from_db(**kwargs)
416
+ self._pedb.logger.info("Getting data from layout database.")
417
+ self.get_variables()
418
+ self.get_materials()
419
+ self.get_stackup()
420
+
421
+ data = {}
422
+ if kwargs.get("general", False):
423
+ data["general"] = self.cfg_data.general.get_data_from_db()
424
+ if kwargs.get("variables", False):
425
+ data["variables"] = self.cfg_data.variables.model_dump(exclude_none=True)
426
+ if kwargs.get("stackup", False):
427
+ data["stackup"] = self.cfg_data.stackup.model_dump(exclude_none=True)
428
+ if kwargs.get("package_definitions", False):
429
+ data["package_definitions"] = self.cfg_data.package_definitions.get_data_from_db()
430
+ if kwargs.get("setups", False):
431
+ setups = self.cfg_data.setups
432
+ setups.retrieve_parameters_from_edb()
433
+ data["setups"] = setups.to_dict()
434
+ if kwargs.get("sources", False):
435
+ data["sources"] = self.cfg_data.sources.get_data_from_db()
436
+ if kwargs.get("ports", False):
437
+ data["ports"] = self.cfg_data.ports.get_data_from_db()
438
+ if kwargs.get("components", False) or kwargs.get("s_parameters", False):
439
+ self.cfg_data.components.retrieve_parameters_from_edb()
440
+ components = []
441
+ for i in self.cfg_data.components.components:
442
+ if i.type == "io":
443
+ components.append(i.get_attributes())
444
+ components.append(i.get_attributes())
445
+
446
+ if kwargs.get("components", False):
447
+ data["components"] = components
448
+ elif kwargs.get("s_parameters", False):
449
+ data["s_parameters"] = self.cfg_data.s_parameters.get_data_from_db(components)
450
+ if kwargs.get("nets", False):
451
+ data["nets"] = self.cfg_data.nets.get_data_from_db()
452
+ if kwargs.get("pin_groups", False):
453
+ data["pin_groups"] = self.cfg_data.pin_groups.get_data_from_db()
454
+ if kwargs.get("operations", False):
455
+ data["operations"] = self.cfg_data.operations.get_data_from_db()
456
+ if kwargs.get("padstacks", False):
457
+ self.cfg_data.padstacks.retrieve_parameters_from_edb()
458
+ definitions = []
459
+ for i in self.cfg_data.padstacks.definitions:
460
+ definitions.append(i.get_attributes())
461
+ instances = []
462
+ for i in self.cfg_data.padstacks.instances:
463
+ instances.append(i.get_attributes())
464
+ data["padstacks"] = dict()
465
+ data["padstacks"]["definitions"] = definitions
466
+ data["padstacks"]["instances"] = instances
467
+
468
+ if kwargs.get("boundaries", False):
469
+ data["boundaries"] = self.cfg_data.boundaries.get_data_from_db()
470
+
471
+ return data
546
472
 
547
473
  def export(
548
474
  self,
@@ -560,6 +486,7 @@ class Configuration:
560
486
  s_parameters=True,
561
487
  padstacks=True,
562
488
  general=True,
489
+ variables=True,
563
490
  ):
564
491
  """Export the configuration data from layout to a file.
565
492
 
@@ -593,12 +520,13 @@ class Configuration:
593
520
  Whether to export padstacks.
594
521
  general : bool
595
522
  Whether to export general information.
523
+ variables : bool
524
+ Whether to export variable.
596
525
  Returns
597
526
  -------
598
527
  bool
599
528
  """
600
- return self.api.export(
601
- file_path,
529
+ data = self.get_data_from_db(
602
530
  stackup=stackup,
603
531
  package_definitions=package_definitions,
604
532
  setups=setups,
@@ -612,4 +540,23 @@ class Configuration:
612
540
  s_parameters=s_parameters,
613
541
  padstacks=padstacks,
614
542
  general=general,
543
+ variables=variables,
615
544
  )
545
+
546
+ file_path = file_path if isinstance(file_path, Path) else Path(file_path)
547
+ file_path = file_path.with_suffix(".json") if file_path.suffix == "" else file_path
548
+
549
+ for comp in data["components"]:
550
+ for key, value in comp.items():
551
+ try:
552
+ json.dumps(value)
553
+ print(f"Key '{key}' is serializable.")
554
+ except TypeError as e:
555
+ print(f"Key '{key}' failed: {e}")
556
+
557
+ with open(file_path, "w") as f:
558
+ if file_path.suffix == ".json":
559
+ json.dump(data, f, ensure_ascii=False, indent=4)
560
+ else:
561
+ toml.dump(data, f)
562
+ return True if os.path.isfile(file_path) else False