pyedb 0.6.0__py3-none-any.whl → 0.7.1__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 (35) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/dotnet/clr_module.py +1 -1
  3. pyedb/dotnet/edb.py +15 -16
  4. pyedb/dotnet/edb_core/cell/hierarchy/model.py +17 -0
  5. pyedb/dotnet/edb_core/components.py +11 -11
  6. pyedb/dotnet/edb_core/configuration.py +346 -77
  7. pyedb/dotnet/edb_core/definition/component_def.py +9 -0
  8. pyedb/dotnet/edb_core/definition/package_def.py +27 -0
  9. pyedb/dotnet/edb_core/edb_data/components_data.py +8 -3
  10. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +14 -13
  11. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +2 -2
  12. pyedb/dotnet/edb_core/edb_data/layer_data.py +8 -3
  13. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +26 -5
  14. pyedb/dotnet/edb_core/edb_data/primitives_data.py +12 -11
  15. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1 -1
  16. pyedb/dotnet/edb_core/edb_data/sources.py +10 -0
  17. pyedb/dotnet/edb_core/hfss.py +1 -1
  18. pyedb/dotnet/edb_core/layout.py +94 -31
  19. pyedb/dotnet/edb_core/materials.py +637 -541
  20. pyedb/dotnet/edb_core/nets.py +5 -5
  21. pyedb/dotnet/edb_core/padstack.py +57 -6
  22. pyedb/dotnet/edb_core/siwave.py +9 -2
  23. pyedb/dotnet/edb_core/stackup.py +108 -94
  24. pyedb/dotnet/edb_core/utilities/__init__.py +3 -0
  25. pyedb/dotnet/edb_core/utilities/heatsink.py +69 -0
  26. pyedb/exceptions.py +6 -0
  27. pyedb/generic/filesystem.py +7 -3
  28. pyedb/generic/general_methods.py +4 -0
  29. pyedb/generic/process.py +4 -1
  30. pyedb/generic/settings.py +10 -0
  31. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/METADATA +31 -53
  32. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/RECORD +35 -32
  33. /pyedb/dotnet/edb_core/{edb_data → utilities}/simulation_setup.py +0 -0
  34. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/LICENSE +0 -0
  35. {pyedb-0.6.0.dist-info → pyedb-0.7.1.dist-info}/WHEEL +0 -0
@@ -21,17 +21,13 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  import json
24
+ import os
24
25
  from pathlib import Path
25
26
 
26
- from pyedb.generic.general_methods import pyedb_function_handler
27
-
27
+ import toml
28
28
 
29
- def load_json(config_file):
30
- if isinstance(config_file, (str, Path)):
31
- with open(config_file, "r") as f:
32
- return json.load(f)
33
- elif isinstance(config_file, dict):
34
- return config_file
29
+ from pyedb.dotnet.edb_core.definition.package_def import PackageDef
30
+ from pyedb.generic.general_methods import pyedb_function_handler
35
31
 
36
32
 
37
33
  class Configuration:
@@ -41,15 +37,17 @@ class Configuration:
41
37
  self._pedb = pedb
42
38
  self._components = self._pedb.components.components
43
39
  self.data = {}
40
+ self._s_parameter_library = ""
41
+ self._spice_model_library = ""
44
42
 
45
43
  @pyedb_function_handler
46
44
  def load(self, config_file, append=True, apply_file=False, output_file=None, open_at_the_end=True):
47
- """Import configuration settings from a JSON file.
45
+ """Import configuration settings from a configure file.
48
46
 
49
47
  Parameters
50
48
  ----------
51
- config_file : str
52
- Full path to json file.
49
+ config_file : str, dict
50
+ Full path to configure file in JSON or TOML format. Dictionary is also supported.
53
51
  append : bool, optional
54
52
  Whether if the new file will append to existing properties or the properties will be cleared before import.
55
53
  Default is ``True`` to keep stored properties
@@ -65,12 +63,29 @@ class Configuration:
65
63
  dict
66
64
  Config dictionary.
67
65
  """
66
+ if isinstance(config_file, dict):
67
+ data = config_file
68
+ elif os.path.isfile(config_file):
69
+ with open(config_file, "r") as f:
70
+ if config_file.endswith(".json"):
71
+ data = json.load(f)
72
+ elif config_file.endswith(".toml"):
73
+ data = toml.load(f)
74
+ else: # pragma: no cover
75
+ return False
68
76
 
69
- data = load_json(config_file)
70
- if not append:
77
+ if not append: # pragma: no cover
71
78
  self.data = {}
72
79
  for k, v in data.items():
73
- self.data[k] = v
80
+ if k in self.data:
81
+ if isinstance(v, list):
82
+ self.data[k].extend(v)
83
+ elif isinstance(v, dict): # pragma: no cover
84
+ self.data[k].update(v)
85
+ else: # pragma: no cover
86
+ self.data[k] = v
87
+ else:
88
+ self.data[k] = v
74
89
  if apply_file:
75
90
  original_file = self._pedb.edbpath
76
91
  if output_file:
@@ -92,9 +107,30 @@ class Configuration:
92
107
  self._pedb.logger.error("No data loaded. Please load a configuration file.")
93
108
  return False
94
109
 
110
+ # Configure general settings
111
+ if "general" in self.data:
112
+ self._load_general()
113
+
114
+ # Configure boundary settings
115
+ if "boundaries" in self.data:
116
+ self._load_boundaries()
117
+
118
+ # Configure nets
119
+ if "nets" in self.data:
120
+ self._load_nets()
121
+
122
+ # Configure components
95
123
  if "components" in self.data:
96
124
  self._load_components()
97
125
 
126
+ # Configure padstacks
127
+ if "padstacks" in self.data:
128
+ self._load_padstacks()
129
+
130
+ # Configure pin groups
131
+ if "pin_groups" in self.data:
132
+ self._load_pin_groups()
133
+
98
134
  # Configure ports
99
135
  if "ports" in self.data:
100
136
  self._load_ports()
@@ -115,6 +151,18 @@ class Configuration:
115
151
  if "s_parameters" in self.data:
116
152
  self._load_s_parameter()
117
153
 
154
+ # Configure SPICE models
155
+ if "spice_models" in self.data:
156
+ self._load_spice_models()
157
+
158
+ # Configure package definitions
159
+ if "package_definitions" in self.data:
160
+ self._load_package_def()
161
+
162
+ # Configure operations
163
+ if "operations" in self.data:
164
+ self._load_operations()
165
+
118
166
  return True
119
167
 
120
168
  @pyedb_function_handler
@@ -122,7 +170,7 @@ class Configuration:
122
170
  """Imports component information from json."""
123
171
 
124
172
  for comp in self.data["components"]:
125
- refdes = comp["reference_designator"]
173
+ ref_designator = comp["reference_designator"]
126
174
  part_type = comp["part_type"].lower()
127
175
  if part_type == "resistor":
128
176
  part_type = "Resistor"
@@ -137,7 +185,7 @@ class Configuration:
137
185
  else:
138
186
  part_type = "Other"
139
187
 
140
- comp_layout = self._components[refdes]
188
+ comp_layout = self._components[ref_designator]
141
189
  comp_layout.type = part_type
142
190
 
143
191
  if part_type in ["Resistor", "Capacitor", "Inductor"]:
@@ -213,7 +261,7 @@ class Configuration:
213
261
  height = solder_ball_properties["height"]
214
262
 
215
263
  self._pedb.components.set_solder_ball(
216
- component=refdes,
264
+ component=ref_designator,
217
265
  sball_diam=diameter,
218
266
  sball_mid_diam=mid_diameter,
219
267
  sball_height=height,
@@ -229,38 +277,50 @@ class Configuration:
229
277
  """Imports port information from json."""
230
278
  for port in self.data["ports"]:
231
279
  port_type = port["type"]
232
- refdes = port["reference_designator"]
233
- comp_layout = self._components[refdes]
234
- pos = port["from"]
235
- if "pin" in pos:
236
- pin_name = pos["pin"]
237
- port_name = "{}_{}".format(refdes, pin_name)
238
- pos_terminal = comp_layout.pins[pin_name].get_terminal(port_name, True)
239
- else: # Net
240
- net_name = pos["net"]
241
- port_name = "{}_{}".format(refdes, net_name)
242
- if port_type == "circuit":
243
- pg_name = "pg_{}".format(port_name)
244
- _, pg = self._pedb.siwave.create_pin_group_on_net(refdes, net_name, pg_name)
245
- pos_terminal = pg.get_terminal(port_name, True)
246
- else: # Coax port
247
- for _, p in comp_layout.pins.items():
248
- if p.net_name == net_name:
249
- pos_terminal = p.get_terminal(port_name, True)
250
- break
280
+
281
+ positive_terminal_json = port["positive_terminal"]
282
+ pos_terminal = ""
283
+ if "pin_group" in positive_terminal_json:
284
+ pin_group = self._pedb.siwave.pin_groups[positive_terminal_json["pin_group"]]
285
+ port_name = pin_group.name if "name" not in port else port["name"]
286
+ pos_terminal = pin_group.get_terminal(port_name, True)
287
+
288
+ else:
289
+ ref_designator = port["reference_designator"]
290
+ comp_layout = self._components[ref_designator]
291
+
292
+ if "pin" in positive_terminal_json:
293
+ pin_name = positive_terminal_json["pin"]
294
+ port_name = "{}_{}".format(ref_designator, pin_name) if "name" not in port else port["name"]
295
+ pos_terminal = comp_layout.pins[pin_name].get_terminal(port_name, True)
296
+ else: # Net
297
+ net_name = positive_terminal_json["net"]
298
+ port_name = "{}_{}".format(ref_designator, net_name) if "name" not in port else port["name"]
299
+ if port_type == "circuit":
300
+ pg_name = "pg_{}".format(port_name)
301
+ _, pg = self._pedb.siwave.create_pin_group_on_net(ref_designator, net_name, pg_name)
302
+ pos_terminal = pg.get_terminal(port_name, True)
303
+ else: # Coax port
304
+ for _, p in comp_layout.pins.items():
305
+ if p.net_name == net_name:
306
+ pos_terminal = p.get_terminal(port_name, True)
307
+ break
251
308
 
252
309
  if port_type == "circuit":
253
- neg = port["to"]
254
- if "pin" in neg:
255
- pin_name = neg["pin"]
256
- port_name = "{}_{}_ref".format(refdes, pin_name)
310
+ negative_terminal_json = port["negative_terminal"]
311
+ if "pin_group" in negative_terminal_json:
312
+ pin_group = self._pedb.siwave.pin_groups[negative_terminal_json["pin_group"]]
313
+ neg_terminal = pin_group.get_terminal(pin_group.name + "_ref", True)
314
+ elif "pin" in negative_terminal_json:
315
+ pin_name = negative_terminal_json["pin"]
316
+ port_name = "{}_{}_ref".format(ref_designator, pin_name)
257
317
  neg_terminal = comp_layout.pins[pin_name].get_terminal(port_name, True)
258
318
  else:
259
- net_name = neg["net"]
260
- port_name = "{}_{}_ref".format(refdes, net_name)
319
+ net_name = negative_terminal_json["net"]
320
+ port_name = "{}_{}_ref".format(ref_designator, net_name)
261
321
  pg_name = "pg_{}".format(port_name)
262
322
  if pg_name not in self._pedb.siwave.pin_groups:
263
- _, pg = self._pedb.siwave.create_pin_group_on_net(refdes, net_name, pg_name)
323
+ _, pg = self._pedb.siwave.create_pin_group_on_net(ref_designator, net_name, pg_name)
264
324
  else:
265
325
  pg = self._pedb.siwave.pin_groups[pg_name]
266
326
  neg_terminal = pg.get_terminal(port_name, True)
@@ -275,36 +335,47 @@ class Configuration:
275
335
 
276
336
  for src in self.data["sources"]:
277
337
  src_type = src["type"]
278
- refdes = src["reference_designator"]
279
338
  name = src["name"]
280
- comp_layout = self._components[refdes]
281
-
282
- pos = src["from"]
283
- if "pin" in pos:
284
- pin_name = pos["pin"]
285
- src_name = name
286
- pos_terminal = comp_layout.pins[pin_name].get_terminal(src_name, True)
287
- elif "net" in pos: # Net
288
- net_name = pos["net"]
289
- src_name = "{}_{}".format(refdes, net_name)
290
- pg_name = "pg_{}".format(src_name)
291
- _, pg = self._pedb.siwave.create_pin_group_on_net(refdes, net_name, pg_name)
292
- pos_terminal = pg.get_terminal(src_name, True)
293
-
294
- neg = src["to"]
295
- if "pin" in neg:
296
- pin_name = neg["pin"]
297
- src_name = name + "_ref"
298
- neg_terminal = comp_layout.pins[pin_name].get_terminal(src_name, True)
299
- elif "net" in neg:
300
- net_name = neg["net"]
301
- src_name = name + "_ref"
302
- pg_name = "pg_{}".format(src_name)
303
- if pg_name not in self._pedb.siwave.pin_groups:
304
- _, pg = self._pedb.siwave.create_pin_group_on_net(refdes, net_name, pg_name)
305
- else: # pragma no cover
306
- pg = self._pedb.siwave.pin_groups[pg_name]
307
- neg_terminal = pg.get_terminal(src_name, True)
339
+
340
+ positive_terminal_json = src["positive_terminal"]
341
+ if "pin_group" in positive_terminal_json:
342
+ pin_group = self._pedb.siwave.pin_groups[positive_terminal_json["pin_group"]]
343
+ pos_terminal = pin_group.get_terminal(pin_group.name, True)
344
+ else:
345
+ ref_designator = src["reference_designator"]
346
+ comp_layout = self._components[ref_designator]
347
+
348
+ if "pin" in positive_terminal_json:
349
+ pin_name = positive_terminal_json["pin"]
350
+ src_name = name
351
+ pos_terminal = comp_layout.pins[pin_name].get_terminal(src_name, True)
352
+ elif "net" in positive_terminal_json: # Net
353
+ net_name = positive_terminal_json["net"]
354
+ src_name = "{}_{}".format(ref_designator, net_name)
355
+ pg_name = "pg_{}".format(src_name)
356
+ _, pg = self._pedb.siwave.create_pin_group_on_net(ref_designator, net_name, pg_name)
357
+ pos_terminal = pg.get_terminal(src_name, True)
358
+
359
+ negative_terminal_json = src["negative_terminal"]
360
+ if "pin_group" in negative_terminal_json:
361
+ pin_group = self._pedb.siwave.pin_groups[negative_terminal_json["pin_group"]]
362
+ neg_terminal = pin_group.get_terminal(pin_group.name + "_ref", True)
363
+ else:
364
+ ref_designator = src["reference_designator"]
365
+ comp_layout = self._components[ref_designator]
366
+ if "pin" in negative_terminal_json:
367
+ pin_name = negative_terminal_json["pin"]
368
+ src_name = name + "_ref"
369
+ neg_terminal = comp_layout.pins[pin_name].get_terminal(src_name, True)
370
+ elif "net" in negative_terminal_json:
371
+ net_name = negative_terminal_json["net"]
372
+ src_name = name + "_ref"
373
+ pg_name = "pg_{}".format(src_name)
374
+ if pg_name not in self._pedb.siwave.pin_groups:
375
+ _, pg = self._pedb.siwave.create_pin_group_on_net(ref_designator, net_name, pg_name)
376
+ else: # pragma no cover
377
+ pg = self._pedb.siwave.pin_groups[pg_name]
378
+ neg_terminal = pg.get_terminal(src_name, True)
308
379
 
309
380
  if src_type == "voltage":
310
381
  src_obj = self._pedb.create_voltage_source(pos_terminal, neg_terminal)
@@ -349,7 +420,10 @@ class Configuration:
349
420
  else:
350
421
  self._pedb.logger.warning("Setup {} already existing. Editing it.".format(name))
351
422
  edb_setup = self._pedb.setups[name]
352
- edb_setup.si_slider_position = setup["si_slider_position"]
423
+ if "si_slider_position" in setup:
424
+ edb_setup.si_slider_position = setup["si_slider_position"]
425
+ if "pi_slider_position" in setup:
426
+ edb_setup.pi_slider_position = setup["pi_slider_position"]
353
427
 
354
428
  if "freq_sweep" in setup:
355
429
  for fsweep in setup["freq_sweep"]:
@@ -430,15 +504,210 @@ class Configuration:
430
504
 
431
505
  for sp in self.data["s_parameters"]:
432
506
  fpath = sp["file_path"]
507
+ if not Path(fpath).anchor:
508
+ fpath = str(Path(self._s_parameter_library) / fpath)
433
509
  sp_name = sp["name"]
434
510
  comp_def_name = sp["component_definition"]
435
511
  comp_def = self._pedb.definitions.component[comp_def_name]
436
512
  comp_def.add_n_port_model(fpath, sp_name)
513
+ comp_list = dict()
514
+ if sp["apply_to_all"]:
515
+ comp_list.update(
516
+ {refdes: comp for refdes, comp in comp_def.components.items() if refdes not in sp["components"]}
517
+ )
518
+ else:
519
+ comp_list.update(
520
+ {refdes: comp for refdes, comp in comp_def.components.items() if refdes in sp["components"]}
521
+ )
522
+
523
+ for refdes, comp in comp_list.items():
524
+ if "reference_net_per_component" in sp:
525
+ ref_net_per_comp = sp["reference_net_per_component"]
526
+ ref_net = ref_net_per_comp[refdes] if refdes in ref_net_per_comp else sp["reference_net"]
527
+ else:
528
+ ref_net = sp["reference_net"]
529
+ comp.use_s_parameter_model(sp_name, reference_net=ref_net)
530
+
531
+ @pyedb_function_handler
532
+ def _load_spice_models(self):
533
+ """Imports SPICE information from json."""
534
+
535
+ for sp in self.data["spice_models"]:
536
+ fpath = sp["file_path"]
537
+ if not Path(fpath).anchor:
538
+ fpath = str(Path(self._spice_model_library) / fpath)
539
+ sp_name = sp["name"]
540
+ sub_circuit_name = sp.get("sub_circuit_name", None)
541
+ comp_def_name = sp["component_definition"]
542
+ comp_def = self._pedb.definitions.component[comp_def_name]
543
+ comps = comp_def.components
437
544
  if sp["apply_to_all"]:
438
- for refdes, comp in comp_def.components.items():
545
+ for refdes, comp in comps.items():
439
546
  if refdes not in sp["components"]:
440
- comp.use_s_parameter_model(sp_name)
547
+ comp.assign_spice_model(fpath, sp_name, sub_circuit_name)
441
548
  else:
442
- for refdes, comp in comp_def.components.items():
549
+ for refdes, comp in comps.items():
443
550
  if refdes in sp["components"]:
444
- comp.use_s_parameter_model(sp_name)
551
+ comp.assign_spice_model(fpath, sp_name, sub_circuit_name)
552
+
553
+ @pyedb_function_handler
554
+ def _load_pin_groups(self):
555
+ """Imports pin groups information from JSON."""
556
+ for pg in self.data["pin_groups"]:
557
+ name = pg["name"]
558
+ ref_designator = pg["reference_designator"]
559
+ if "pins" in pg:
560
+ self._pedb.siwave.create_pin_group(ref_designator, pg["pins"], name)
561
+ elif "net" in pg:
562
+ self._pedb.siwave.create_pin_group_on_net(ref_designator, pg["net"], name)
563
+
564
+ @pyedb_function_handler
565
+ def _load_nets(self):
566
+ """Imports nets information from JSON."""
567
+ nets = self._pedb.nets.nets
568
+ for i in self.data["nets"]["power_ground_nets"]:
569
+ nets[i].is_power_ground = True
570
+
571
+ for i in self.data["nets"]["signal_nets"]:
572
+ nets[i].is_power_ground = False
573
+
574
+ @pyedb_function_handler
575
+ def _load_general(self):
576
+ """Imports general information from JSON."""
577
+ general = self.data["general"]
578
+ if "s_parameter_library" in general:
579
+ self._s_parameter_library = general["s_parameter_library"]
580
+ if "spice_model_library" in general:
581
+ self._spice_model_library = general["spice_model_library"]
582
+
583
+ @pyedb_function_handler
584
+ def _load_boundaries(self):
585
+ """Imports boundary information from JSON."""
586
+ boundaries = self.data["boundaries"]
587
+
588
+ open_region = boundaries.get("open_region", None)
589
+ if open_region:
590
+ self._pedb.hfss.hfss_extent_info.use_open_region = open_region
591
+
592
+ open_region_type = boundaries.get("open_region_type", None)
593
+ if open_region_type:
594
+ self._pedb.hfss.hfss_extent_info.open_region_type = open_region_type
595
+
596
+ pml_visible = boundaries.get("pml_visible", None)
597
+ if pml_visible:
598
+ self._pedb.hfss.hfss_extent_info.is_pml_visible = pml_visible
599
+
600
+ pml_operation_frequency = boundaries.get("pml_operation_frequency", None)
601
+ if pml_operation_frequency:
602
+ self._pedb.hfss.hfss_extent_info.operating_freq = pml_operation_frequency
603
+
604
+ pml_radiation_factor = boundaries.get("pml_radiation_factor", None)
605
+ if pml_radiation_factor:
606
+ self._pedb.hfss.hfss_extent_info.radiation_level = pml_radiation_factor
607
+
608
+ dielectric_extents_type = boundaries.get("dielectric_extents_type", None)
609
+ if dielectric_extents_type:
610
+ self._pedb.hfss.hfss_extent_info.extent_type = dielectric_extents_type
611
+
612
+ dielectric_base_polygon = boundaries.get("dielectric_base_polygon", None)
613
+ if dielectric_base_polygon:
614
+ self._pedb.hfss.hfss_extent_info.dielectric_base_polygon = dielectric_base_polygon
615
+
616
+ horizontal_padding = boundaries.get("horizontal_padding", None)
617
+ if horizontal_padding:
618
+ self._pedb.hfss.hfss_extent_info.dielectric_extent_size = horizontal_padding
619
+
620
+ honor_primitives_on_dielectric_layers = boundaries.get("honor_primitives_on_dielectric_layers", None)
621
+ if honor_primitives_on_dielectric_layers:
622
+ self._pedb.hfss.hfss_extent_info.honor_user_dielectric = honor_primitives_on_dielectric_layers
623
+
624
+ air_box_extents_type = boundaries.get("air_box_extents_type", None)
625
+ if air_box_extents_type:
626
+ self._pedb.hfss.hfss_extent_info.extent_type = air_box_extents_type
627
+
628
+ air_box_truncate_model_ground_layers = boundaries.get("air_box_truncate_model_ground_layers", None)
629
+ if air_box_truncate_model_ground_layers:
630
+ self._pedb.hfss.hfss_extent_info.truncate_air_box_at_ground = air_box_truncate_model_ground_layers
631
+
632
+ air_box_horizontal_padding = boundaries.get("air_box_horizontal_padding", None)
633
+ if air_box_horizontal_padding:
634
+ self._pedb.hfss.hfss_extent_info.air_box_horizontal_extent = air_box_horizontal_padding
635
+
636
+ air_box_positive_vertical_padding = boundaries.get("air_box_positive_vertical_padding", None)
637
+ if air_box_positive_vertical_padding:
638
+ self._pedb.hfss.hfss_extent_info.air_box_positive_vertical_extent = air_box_positive_vertical_padding
639
+
640
+ air_box_negative_vertical_padding = boundaries.get("air_box_negative_vertical_padding", None)
641
+ if air_box_positive_vertical_padding:
642
+ self._pedb.hfss.hfss_extent_info.air_box_negative_vertical_extent = air_box_negative_vertical_padding
643
+
644
+ @pyedb_function_handler
645
+ def _load_operations(self):
646
+ """Imports operation information from JSON."""
647
+ operations = self.data["operations"]
648
+ cutout = operations.get("cutout", None)
649
+ if cutout:
650
+ self._pedb.cutout(**cutout)
651
+
652
+ @pyedb_function_handler
653
+ def _load_padstacks(self):
654
+ """Imports padstack information from JSON."""
655
+ padstacks = self.data["padstacks"]
656
+ definitions = padstacks.get("definitions", None)
657
+ if definitions:
658
+ padstack_defs = self._pedb.padstacks.definitions
659
+ for value in definitions:
660
+ pdef = padstack_defs[value["name"]]
661
+ if "hole_diameter" in value:
662
+ pdef.hole_diameter = value["hole_diameter"]
663
+ if "hole_plating_thickness" in value:
664
+ pdef.hole_plating_thickness = value["hole_plating_thickness"]
665
+ if "hole_material" in value:
666
+ pdef.material = value["hole_material"]
667
+ if "hole_range" in value:
668
+ pdef.hole_range = value["hole_range"]
669
+ instances = padstacks.get("instances", None)
670
+ if instances:
671
+ padstack_instances = self._pedb.padstacks.instances_by_name
672
+ for value in instances:
673
+ inst = padstack_instances[value["name"]]
674
+ backdrill_top = value.get("backdrill_top", None)
675
+ if backdrill_top:
676
+ inst.set_backdrill_top(
677
+ backdrill_top["drill_to_layer"], backdrill_top["drill_diameter"], backdrill_top["stub_length"]
678
+ )
679
+ backdrill_bottom = value.get("backdrill_bottom", None)
680
+ if backdrill_top:
681
+ inst.set_backdrill_bottom(
682
+ backdrill_bottom["drill_to_layer"],
683
+ backdrill_bottom["drill_diameter"],
684
+ backdrill_bottom["stub_length"],
685
+ )
686
+
687
+ @pyedb_function_handler
688
+ def _load_package_def(self):
689
+ """Imports package definition information from JSON."""
690
+ comps = self._pedb.components.components
691
+ for pkgd in self.data["package_definitions"]:
692
+ name = pkgd["name"]
693
+ if name in self._pedb.definitions.package:
694
+ self._pedb.definitions.package[name].delete()
695
+ package_def = PackageDef(self._pedb, name=name)
696
+ package_def.maximum_power = pkgd["maximum_power"]
697
+ package_def.therm_cond = pkgd["therm_cond"]
698
+ package_def.theta_jb = pkgd["theta_jb"]
699
+ package_def.theta_jc = pkgd["theta_jc"]
700
+ package_def.height = pkgd["height"]
701
+
702
+ heatsink = pkgd.get("heatsink", None)
703
+ if heatsink:
704
+ package_def.set_heatsink(
705
+ heatsink["fin_base_height"],
706
+ heatsink["fin_height"],
707
+ heatsink["fin_orientation"],
708
+ heatsink["fin_spacing"],
709
+ heatsink["fin_thickness"],
710
+ )
711
+ json_comps = pkgd["components"] if isinstance(pkgd["components"], list) else [pkgd["components"]]
712
+ for i in json_comps:
713
+ comps[i].package_def = name
@@ -152,6 +152,15 @@ class EDBComponentDef(ObjBase):
152
152
  comp.assign_spice_model(file_path, model_name)
153
153
  return True
154
154
 
155
+ @property
156
+ def reference_file(self):
157
+ ref_files = []
158
+ for comp_model in self._comp_model:
159
+ model_type = str(comp_model.GetComponentModelType())
160
+ if model_type == "NPortComponentModel" or model_type == "DynamicLinkComponentModel":
161
+ ref_files.append(comp_model.GetReferenceFile())
162
+ return ref_files
163
+
155
164
  @property
156
165
  def component_models(self):
157
166
  temp = {}
@@ -67,6 +67,11 @@ class PackageDef(ObjBase):
67
67
  edb_object.SetExteriorBoundary(polygon)
68
68
  return edb_object
69
69
 
70
+ @pyedb_function_handler
71
+ def delete(self):
72
+ """Delete a package definition object from the database."""
73
+ return self._edb_object.Delete()
74
+
70
75
  @property
71
76
  def maximum_power(self):
72
77
  """Maximum power of the package."""
@@ -116,3 +121,25 @@ class PackageDef(ObjBase):
116
121
  def height(self, value):
117
122
  value = self._pedb.edb_value(value)
118
123
  self._edb_object.SetHeight(value)
124
+
125
+ @pyedb_function_handler
126
+ def set_heatsink(self, fin_base_height, fin_height, fin_orientation, fin_spacing, fin_thickness):
127
+ from pyedb.dotnet.edb_core.utilities.heatsink import HeatSink
128
+
129
+ heatsink = HeatSink(self._pedb)
130
+ heatsink.fin_base_height = fin_base_height
131
+ heatsink.fin_height = fin_height
132
+ heatsink.fin_orientation = fin_orientation
133
+ heatsink.fin_spacing = fin_spacing
134
+ heatsink.fin_thickness = fin_thickness
135
+ self._edb_object.SetHeatSink(heatsink._edb_object)
136
+
137
+ @property
138
+ def heatsink(self):
139
+ from pyedb.dotnet.edb_core.utilities.heatsink import HeatSink
140
+
141
+ flag, edb_object = self._edb_object.GetHeatSink()
142
+ if flag:
143
+ return HeatSink(self._pedb, edb_object)
144
+ else:
145
+ return None
@@ -24,7 +24,7 @@ import logging
24
24
  import re
25
25
  import warnings
26
26
 
27
- from pyedb.dotnet.edb_core.cell.hierarchy.model import PinPairModel
27
+ from pyedb.dotnet.edb_core.cell.hierarchy.model import PinPairModel, SPICEModel
28
28
  from pyedb.dotnet.edb_core.definition.package_def import PackageDef
29
29
  from pyedb.dotnet.edb_core.edb_data.padstacks_data import EDBPadstackInstance
30
30
  from pyedb.generic.general_methods import is_ironpython
@@ -222,6 +222,8 @@ class EDBComponent(object):
222
222
  model_type = edb_object.ToString().split(".")[-1]
223
223
  if model_type == "PinPairModel":
224
224
  return PinPairModel(self._pedb, edb_object)
225
+ elif model_type == "SPICEModel":
226
+ return SPICEModel(self._pedb, edb_object)
225
227
 
226
228
  @model.setter
227
229
  def model(self, value):
@@ -373,7 +375,7 @@ class EDBComponent(object):
373
375
 
374
376
  @property
375
377
  def solder_ball_diameter(self):
376
- """Solder ball diameter"""
378
+ """Solder ball diameter."""
377
379
  if "GetSolderBallProperty" in dir(self.component_property):
378
380
  result = self.component_property.GetSolderBallProperty().GetDiameter()
379
381
  succeed = result[0]
@@ -848,6 +850,7 @@ class EDBComponent(object):
848
850
  @property
849
851
  def is_top_mounted(self):
850
852
  """Check if a component is mounted on top or bottom of the layout.
853
+
851
854
  Returns
852
855
  -------
853
856
  bool
@@ -912,7 +915,7 @@ class EDBComponent(object):
912
915
  return True
913
916
 
914
917
  @pyedb_function_handler()
915
- def assign_spice_model(self, file_path, name=None):
918
+ def assign_spice_model(self, file_path, name=None, sub_circuit_name=None):
916
919
  """Assign Spice model to this component.
917
920
 
918
921
  Parameters
@@ -940,6 +943,8 @@ class EDBComponent(object):
940
943
  model = self._edb.cell.hierarchy._hierarchy.SPICEModel()
941
944
  model.SetModelPath(file_path)
942
945
  model.SetModelName(name)
946
+ if sub_circuit_name:
947
+ model.SetSubCkt(sub_circuit_name)
943
948
  terminal = 1
944
949
  for pn in pinNames:
945
950
  model.AddTerminalPinPair(pn, str(terminal))