pyedb 0.2.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 (128) hide show
  1. pyedb/__init__.py +17 -0
  2. pyedb/dotnet/__init__.py +0 -0
  3. pyedb/dotnet/application/Variables.py +2261 -0
  4. pyedb/dotnet/application/__init__.py +0 -0
  5. pyedb/dotnet/clr_module.py +103 -0
  6. pyedb/dotnet/edb.py +4237 -0
  7. pyedb/dotnet/edb_core/__init__.py +1 -0
  8. pyedb/dotnet/edb_core/cell/__init__.py +0 -0
  9. pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
  10. pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
  11. pyedb/dotnet/edb_core/components.py +2669 -0
  12. pyedb/dotnet/edb_core/configuration.py +423 -0
  13. pyedb/dotnet/edb_core/definition/__init__.py +0 -0
  14. pyedb/dotnet/edb_core/definition/component_def.py +166 -0
  15. pyedb/dotnet/edb_core/definition/component_model.py +30 -0
  16. pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
  17. pyedb/dotnet/edb_core/definition/definitions.py +12 -0
  18. pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
  19. pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
  20. pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
  21. pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
  22. pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
  23. pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
  24. pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
  25. pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
  26. pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
  27. pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
  28. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
  29. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
  30. pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
  31. pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
  32. pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
  33. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
  34. pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
  35. pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
  36. pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
  37. pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
  38. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
  39. pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
  40. pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
  41. pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
  42. pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
  43. pyedb/dotnet/edb_core/general.py +181 -0
  44. pyedb/dotnet/edb_core/hfss.py +1646 -0
  45. pyedb/dotnet/edb_core/layout.py +1244 -0
  46. pyedb/dotnet/edb_core/layout_validation.py +272 -0
  47. pyedb/dotnet/edb_core/materials.py +939 -0
  48. pyedb/dotnet/edb_core/net_class.py +335 -0
  49. pyedb/dotnet/edb_core/nets.py +1215 -0
  50. pyedb/dotnet/edb_core/padstack.py +1389 -0
  51. pyedb/dotnet/edb_core/siwave.py +1427 -0
  52. pyedb/dotnet/edb_core/stackup.py +2703 -0
  53. pyedb/edb_logger.py +396 -0
  54. pyedb/generic/__init__.py +0 -0
  55. pyedb/generic/constants.py +1063 -0
  56. pyedb/generic/data_handlers.py +320 -0
  57. pyedb/generic/design_types.py +104 -0
  58. pyedb/generic/filesystem.py +150 -0
  59. pyedb/generic/general_methods.py +1535 -0
  60. pyedb/generic/plot.py +1840 -0
  61. pyedb/generic/process.py +285 -0
  62. pyedb/generic/settings.py +224 -0
  63. pyedb/ipc2581/__init__.py +0 -0
  64. pyedb/ipc2581/bom/__init__.py +0 -0
  65. pyedb/ipc2581/bom/bom.py +21 -0
  66. pyedb/ipc2581/bom/bom_item.py +32 -0
  67. pyedb/ipc2581/bom/characteristics.py +37 -0
  68. pyedb/ipc2581/bom/refdes.py +16 -0
  69. pyedb/ipc2581/content/__init__.py +0 -0
  70. pyedb/ipc2581/content/color.py +38 -0
  71. pyedb/ipc2581/content/content.py +55 -0
  72. pyedb/ipc2581/content/dictionary_color.py +29 -0
  73. pyedb/ipc2581/content/dictionary_fill.py +28 -0
  74. pyedb/ipc2581/content/dictionary_line.py +30 -0
  75. pyedb/ipc2581/content/entry_color.py +13 -0
  76. pyedb/ipc2581/content/entry_line.py +14 -0
  77. pyedb/ipc2581/content/fill.py +15 -0
  78. pyedb/ipc2581/content/layer_ref.py +10 -0
  79. pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
  80. pyedb/ipc2581/ecad/__init__.py +0 -0
  81. pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
  82. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
  83. pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
  84. pyedb/ipc2581/ecad/cad_data/component.py +41 -0
  85. pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
  86. pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
  87. pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
  88. pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
  89. pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
  90. pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
  91. pyedb/ipc2581/ecad/cad_data/package.py +104 -0
  92. pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
  93. pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
  94. pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
  95. pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
  96. pyedb/ipc2581/ecad/cad_data/path.py +89 -0
  97. pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
  98. pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
  99. pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
  100. pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
  101. pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
  102. pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
  103. pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
  104. pyedb/ipc2581/ecad/cad_data/step.py +275 -0
  105. pyedb/ipc2581/ecad/cad_header.py +33 -0
  106. pyedb/ipc2581/ecad/ecad.py +19 -0
  107. pyedb/ipc2581/ecad/spec.py +46 -0
  108. pyedb/ipc2581/history_record.py +37 -0
  109. pyedb/ipc2581/ipc2581.py +387 -0
  110. pyedb/ipc2581/logistic_header.py +25 -0
  111. pyedb/misc/__init__.py +0 -0
  112. pyedb/misc/aedtlib_personalib_install.py +14 -0
  113. pyedb/misc/downloads.py +322 -0
  114. pyedb/misc/misc.py +67 -0
  115. pyedb/misc/pyedb.runtimeconfig.json +13 -0
  116. pyedb/misc/siw_feature_config/__init__.py +0 -0
  117. pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
  118. pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
  119. pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
  120. pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
  121. pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
  122. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
  123. pyedb/misc/utilities.py +27 -0
  124. pyedb/modeler/geometry_operators.py +2082 -0
  125. pyedb-0.2.0.dist-info/LICENSE +21 -0
  126. pyedb-0.2.0.dist-info/METADATA +208 -0
  127. pyedb-0.2.0.dist-info/RECORD +128 -0
  128. pyedb-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,1268 @@
1
+ import copy
2
+ import os
3
+ import re
4
+ import sys
5
+
6
+ from pyedb.edb_logger import pyedb_logger
7
+ from pyedb.generic.general_methods import (
8
+ ET,
9
+ env_path,
10
+ env_value,
11
+ is_ironpython,
12
+ is_linux,
13
+ pyedb_function_handler,
14
+ )
15
+ from pyedb.misc.aedtlib_personalib_install import write_pretty_xml
16
+ from pyedb.misc.misc import list_installed_ansysem
17
+
18
+ if is_linux and is_ironpython:
19
+ import subprocessdotnet as subprocess
20
+ else:
21
+ import subprocess
22
+
23
+
24
+ @pyedb_function_handler()
25
+ def convert_technology_file(tech_file, edbversion=None, control_file=None):
26
+ """Convert a technology file to edb control file (xml).
27
+
28
+ Parameters
29
+ ----------
30
+ tech_file : str
31
+ Full path to technology file
32
+ edbversion : str, optional
33
+ Edb version to use. Default is `None` to use latest available version of Edb.
34
+ control_file : str, optional
35
+ Control file output file. Default is `None` to use same path and same name of `tech_file`.
36
+
37
+ Returns
38
+ -------
39
+ str
40
+ Control file full path if created.
41
+ """
42
+ if is_linux: # pragma: no cover
43
+ if not edbversion:
44
+ edbversion = "20{}.{}".format(list_installed_ansysem()[0][-3:-1], list_installed_ansysem()[0][-1:])
45
+ if env_value(edbversion) in os.environ:
46
+ base_path = env_path(edbversion)
47
+ sys.path.append(base_path)
48
+ else:
49
+ pyedb_logger.error("No Edb installation found. Check environment variables")
50
+ return False
51
+ os.environ["HELIC_ROOT"] = os.path.join(base_path, "helic")
52
+ if os.getenv("ANSYSLMD_LICENCE_FILE", None) is None:
53
+ lic = os.path.join(base_path, "..", "..", "shared_files", "licensing", "ansyslmd.ini")
54
+ if os.path.exists(lic):
55
+ with open(lic, "r") as fh:
56
+ lines = fh.read().splitlines()
57
+ for line in lines:
58
+ if line.startswith("SERVER="):
59
+ os.environ["ANSYSLMD_LICENSE_FILE"] = line.split("=")[1]
60
+ break
61
+ else:
62
+ pyedb_logger.error("ANSYSLMD_LICENSE_FILE is not defined.")
63
+ vlc_file_name = os.path.splitext(tech_file)[0]
64
+ if not control_file:
65
+ control_file = vlc_file_name + ".xml"
66
+ vlc_file = vlc_file_name + ".vlc.tech"
67
+ commands = []
68
+ command = [
69
+ os.path.join(base_path, "helic", "tools", "bin", "afet", "tech2afet"),
70
+ "-i",
71
+ tech_file,
72
+ "-o",
73
+ vlc_file,
74
+ "--backplane",
75
+ "False",
76
+ ]
77
+ commands.append(command)
78
+ command = [
79
+ os.path.join(base_path, "helic", "tools", "raptorh", "bin", "make-edb"),
80
+ "--dielectric-simplification-method",
81
+ "1",
82
+ "-t",
83
+ vlc_file,
84
+ "-o",
85
+ vlc_file_name,
86
+ "--export-xml",
87
+ control_file,
88
+ ]
89
+ commands.append(command)
90
+ commands.append(["rm", "-r", vlc_file_name + ".aedb"])
91
+ my_env = os.environ.copy()
92
+ for command in commands:
93
+ p = subprocess.Popen(command, env=my_env)
94
+ p.wait()
95
+ if os.path.exists(control_file):
96
+ pyedb_logger.info("Xml file created.")
97
+ return control_file
98
+ pyedb_logger.error("Technology files are supported only in Linux. Use control file instead.")
99
+ return False
100
+
101
+
102
+ class ControlProperty:
103
+ def __init__(self, property_name, value):
104
+ self.name = property_name
105
+ self.value = value
106
+ if isinstance(value, str):
107
+ self.type = 1
108
+ elif isinstance(value, list):
109
+ self.type = 2
110
+ else:
111
+ try:
112
+ float(value)
113
+ self.type = 0
114
+ except TypeError:
115
+ pass
116
+
117
+ def _write_xml(self, root):
118
+ try:
119
+ if self.type == 0:
120
+ content = ET.SubElement(root, self.name)
121
+ double = ET.SubElement(content, "Double")
122
+ double.text = str(self.value)
123
+ else:
124
+ pass
125
+ except:
126
+ pass
127
+
128
+
129
+ class ControlFileMaterial:
130
+ def __init__(self, name, properties):
131
+ self.name = name
132
+ self.properties = {}
133
+ for name, property in properties.items():
134
+ self.properties[name] = ControlProperty(name, property)
135
+
136
+ def _write_xml(self, root):
137
+ content = ET.SubElement(root, "Material")
138
+ content.set("Name", self.name)
139
+ for property_name, property in self.properties.items():
140
+ property._write_xml(content)
141
+
142
+
143
+ class ControlFileDielectric:
144
+ def __init__(self, name, properties):
145
+ self.name = name
146
+ self.properties = {}
147
+ for name, prop in properties.items():
148
+ self.properties[name] = prop
149
+
150
+ def _write_xml(self, root):
151
+ content = ET.SubElement(root, "Layer")
152
+ for property_name, property in self.properties.items():
153
+ if not property_name == "Index":
154
+ content.set(property_name, str(property))
155
+
156
+
157
+ class ControlFileLayer:
158
+ def __init__(self, name, properties):
159
+ self.name = name
160
+ self.properties = {}
161
+ for name, prop in properties.items():
162
+ self.properties[name] = prop
163
+
164
+ def _write_xml(self, root):
165
+ content = ET.SubElement(root, "Layer")
166
+ content.set("Color", self.properties.get("Color", "#5c4300"))
167
+ if self.properties.get("Elevation"):
168
+ content.set("Elevation", self.properties["Elevation"])
169
+ if self.properties.get("GDSDataType"):
170
+ content.set("GDSDataType", self.properties["GDSDataType"])
171
+ if self.properties.get("GDSIIVia") or self.properties.get("GDSDataType"):
172
+ content.set("GDSIIVia", self.properties.get("GDSIIVia", "false"))
173
+ if self.properties.get("Material"):
174
+ content.set("Material", self.properties.get("Material", "air"))
175
+ content.set("Name", self.name)
176
+ if self.properties.get("StartLayer"):
177
+ content.set("StartLayer", self.properties["StartLayer"])
178
+ if self.properties.get("StopLayer"):
179
+ content.set("StopLayer", self.properties["StopLayer"])
180
+ if self.properties.get("TargetLayer"):
181
+ content.set("TargetLayer", self.properties["TargetLayer"])
182
+ if self.properties.get("Thickness"):
183
+ content.set("Thickness", self.properties.get("Thickness", "0.001"))
184
+ if self.properties.get("Type"):
185
+ content.set("Type", self.properties.get("Type", "conductor"))
186
+
187
+
188
+ class ControlFileVia(ControlFileLayer):
189
+ def __init__(self, name, properties):
190
+ ControlFileLayer.__init__(self, name, properties)
191
+ self.create_via_group = False
192
+ self.check_containment = True
193
+ self.method = "proximity"
194
+ self.persistent = False
195
+ self.tolerance = "1um"
196
+ self.snap_via_groups = False
197
+ self.snap_method = "areaFactor"
198
+ self.remove_unconnected = True
199
+ self.snap_tolerance = 3
200
+
201
+ def _write_xml(self, root):
202
+ content = ET.SubElement(root, "Layer")
203
+ content.set("Color", self.properties.get("Color", "#5c4300"))
204
+ if self.properties.get("Elevation"):
205
+ content.set("Elevation", self.properties["Elevation"])
206
+ if self.properties.get("GDSDataType"):
207
+ content.set("GDSDataType", self.properties["GDSDataType"])
208
+ if self.properties.get("Material"):
209
+ content.set("Material", self.properties.get("Material", "air"))
210
+ content.set("Name", self.name)
211
+ content.set("StartLayer", self.properties.get("StartLayer", ""))
212
+ content.set("StopLayer", self.properties.get("StopLayer", ""))
213
+ if self.properties.get("TargetLayer"):
214
+ content.set("TargetLayer", self.properties["TargetLayer"])
215
+ if self.properties.get("Thickness"):
216
+ content.set("Thickness", self.properties.get("Thickness", "0.001"))
217
+ if self.properties.get("Type"):
218
+ content.set("Type", self.properties.get("Type", "conductor"))
219
+ if self.create_via_group:
220
+ viagroup = ET.SubElement(content, "CreateViaGroups")
221
+ viagroup.set("CheckContainment", "true" if self.check_containment else "false")
222
+ viagroup.set("Method", self.method)
223
+ viagroup.set("Persistent", "true" if self.persistent else "false")
224
+ viagroup.set("Tolerance", self.tolerance)
225
+ if self.snap_via_groups:
226
+ snapgroup = ET.SubElement(content, "SnapViaGroups")
227
+ snapgroup.set("Method", self.snap_method)
228
+ snapgroup.set("RemoveUnconnected", "true" if self.remove_unconnected else "false")
229
+ snapgroup.set("Tolerance", str(self.snap_tolerance))
230
+
231
+
232
+ class ControlFileStackup:
233
+ """Class that manages the Stackup info."""
234
+
235
+ def __init__(self, units="mm"):
236
+ self._materials = {}
237
+ self._layers = []
238
+ self._dielectrics = []
239
+ self._vias = []
240
+ self.units = units
241
+ self.metal_layer_snapping_tolerance = None
242
+ self.dielectrics_base_elevation = 0
243
+
244
+ @property
245
+ def vias(self):
246
+ """Via list.
247
+
248
+ Returns
249
+ -------
250
+ list of :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileVia`
251
+
252
+ """
253
+ return self._vias
254
+
255
+ @property
256
+ def materials(self):
257
+ """Material list.
258
+
259
+ Returns
260
+ -------
261
+ list of :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileMaterial`
262
+
263
+ """
264
+ return self._materials
265
+
266
+ @property
267
+ def dielectrics(self):
268
+ """Dielectric layer list.
269
+
270
+ Returns
271
+ -------
272
+ list of :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileLayer`
273
+
274
+ """
275
+ return self._dielectrics
276
+
277
+ @property
278
+ def layers(self):
279
+ """Layer list.
280
+
281
+ Returns
282
+ -------
283
+ list of :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileLayer`
284
+
285
+ """
286
+ return self._layers
287
+
288
+ def add_material(
289
+ self,
290
+ material_name,
291
+ permittivity=1.0,
292
+ dielectric_loss_tg=0.0,
293
+ permeability=1.0,
294
+ conductivity=0.0,
295
+ properties=None,
296
+ ):
297
+ """Add a new material with specific properties.
298
+
299
+ Parameters
300
+ ----------
301
+ material_name : str
302
+ Material name.
303
+ permittivity : float, optional
304
+ Material permittivity. The default is ``1.0``.
305
+ dielectric_loss_tg : float, optional
306
+ Material tangent losses. The default is ``0.0``.
307
+ permeability : float, optional
308
+ Material permeability. The default is ``1.0``.
309
+ conductivity : float, optional
310
+ Material conductivity. The default is ``0.0``.
311
+ properties : dict, optional
312
+ Specific material properties. The default is ``None``.
313
+ Dictionary with key and material property value.
314
+
315
+ Returns
316
+ -------
317
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileMaterial`
318
+ """
319
+ if isinstance(properties, dict):
320
+ self._materials[material_name] = ControlFileMaterial(material_name, properties)
321
+ return self._materials[material_name]
322
+ else:
323
+ properties = {
324
+ "Name": material_name,
325
+ "Permittivity": permittivity,
326
+ "Permeability": permeability,
327
+ "Conductivity": conductivity,
328
+ "DielectricLossTangent": dielectric_loss_tg,
329
+ }
330
+ self._materials[material_name] = ControlFileMaterial(material_name, properties)
331
+ return self._materials[material_name]
332
+
333
+ def add_layer(
334
+ self,
335
+ layer_name,
336
+ elevation=0.0,
337
+ material="",
338
+ gds_type=0,
339
+ target_layer="",
340
+ thickness=0.0,
341
+ layer_type="conductor",
342
+ solve_inside=True,
343
+ properties=None,
344
+ ):
345
+ """Add a new layer.
346
+
347
+ Parameters
348
+ ----------
349
+ layer_name : str
350
+ Layer name.
351
+ elevation : float
352
+ Layer elevation.
353
+ material : str
354
+ Material for the layer.
355
+ gds_type : int
356
+ GDS type assigned on the layer. The value must be the same as in the GDS file otherwise geometries won't be
357
+ imported.
358
+ target_layer : str
359
+ Layer name assigned in EDB or HFSS 3D layout after import.
360
+ thickness : float
361
+ Layer thickness
362
+ layer_type : str
363
+ Define the layer type, default value for a layer is ``"conductor"``
364
+ solve_inside : bool
365
+ When ``True`` solver will solve inside metal, and not id ``False``. Default value is ``True``.
366
+ properties : dict
367
+ Dictionary with key and property value.
368
+
369
+ Returns
370
+ -------
371
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileLayer`
372
+ """
373
+ if isinstance(properties, dict):
374
+ self._layers.append(ControlFileLayer(layer_name, properties))
375
+ return self._layers[-1]
376
+ else:
377
+ properties = {
378
+ "Name": layer_name,
379
+ "GDSDataType": str(gds_type),
380
+ "TargetLayer": target_layer,
381
+ "Type": layer_type,
382
+ "Material": material,
383
+ "Thickness": str(thickness),
384
+ "Elevation": str(elevation),
385
+ "SolveInside": str(solve_inside).lower(),
386
+ }
387
+ self._layers.append(ControlFileDielectric(layer_name, properties))
388
+ return self._layers[-1]
389
+
390
+ def add_dielectric(
391
+ self,
392
+ layer_name,
393
+ layer_index=None,
394
+ material="",
395
+ thickness=0.0,
396
+ properties=None,
397
+ base_layer=None,
398
+ add_on_top=True,
399
+ ):
400
+ """Add a new dielectric.
401
+
402
+ Parameters
403
+ ----------
404
+ layer_name : str
405
+ Layer name.
406
+ layer_index : int, optional
407
+ Dielectric layer index as they must be stacked. If not provided the layer index will be incremented.
408
+ material : str
409
+ Material name.
410
+ thickness : float
411
+ Layer thickness.
412
+ properties : dict
413
+ Dictionary with key and property value.
414
+ base_layer : str, optional
415
+ Layer name used for layer placement. Default value is ``None``. This option is used for inserting
416
+ dielectric layer between two existing ones. When no argument is provided the dielectric layer will be placed
417
+ on top of the stacked ones.
418
+ method : bool, Optional.
419
+ Provides the method to use when the argument ``base_layer`` is provided. When ``True`` the layer is added
420
+ on top on the base layer, when ``False`` it will be added below.
421
+
422
+ Returns
423
+ -------
424
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileDielectric`
425
+ """
426
+ if isinstance(properties, dict):
427
+ self._dielectrics.append(ControlFileDielectric(layer_name, properties))
428
+ return self._dielectrics[-1]
429
+ else:
430
+ if not layer_index and self.dielectrics and not base_layer:
431
+ layer_index = max([diel.properties["Index"] for diel in self.dielectrics]) + 1
432
+ elif base_layer and self.dielectrics:
433
+ if base_layer in [diel.properties["Name"] for diel in self.dielectrics]:
434
+ base_layer_index = next(
435
+ diel.properties["Index"] for diel in self.dielectrics if diel.properties["Name"] == base_layer
436
+ )
437
+ if add_on_top:
438
+ layer_index = base_layer_index + 1
439
+ for diel_layer in self.dielectrics:
440
+ if diel_layer.properties["Index"] > base_layer_index:
441
+ diel_layer.properties["Index"] += 1
442
+ else:
443
+ layer_index = base_layer_index
444
+ for diel_layer in self.dielectrics:
445
+ if diel_layer.properties["Index"] >= base_layer_index:
446
+ diel_layer.properties["Index"] += 1
447
+ elif not layer_index:
448
+ layer_index = 0
449
+ properties = {"Index": layer_index, "Material": material, "Name": layer_name, "Thickness": thickness}
450
+ self._dielectrics.append(ControlFileDielectric(layer_name, properties))
451
+ return self._dielectrics[-1]
452
+
453
+ def add_via(
454
+ self,
455
+ layer_name,
456
+ material="",
457
+ gds_type=0,
458
+ target_layer="",
459
+ start_layer="",
460
+ stop_layer="",
461
+ solve_inside=True,
462
+ via_group_method="proximity",
463
+ via_group_tol=1e-6,
464
+ via_group_persistent=True,
465
+ snap_via_group_method="distance",
466
+ snap_via_group_tol=10e-9,
467
+ properties=None,
468
+ ):
469
+ """Add a new via layer.
470
+
471
+ Parameters
472
+ ----------
473
+ layer_name : str
474
+ Layer name.
475
+ material : str
476
+ Define the material for this layer.
477
+ gds_type : int
478
+ Define the gds type.
479
+ target_layer : str
480
+ Target layer used after layout import in EDB and HFSS 3D layout.
481
+ start_layer : str
482
+ Define the start layer for the via
483
+ stop_layer : str
484
+ Define the stop layer for the via.
485
+ solve_inside : bool
486
+ When ``True`` solve inside this layer is anbled. Default value is ``True``.
487
+ via_group_method : str
488
+ Define the via group method, default value is ``"proximity"``
489
+ via_group_tol : float
490
+ Define the via group tolerance.
491
+ via_group_persistent : bool
492
+ When ``True`` activated otherwise when ``False``is deactivated. Default value is ``True``.
493
+ snap_via_group_method : str
494
+ Define the via group method, default value is ``"distance"``
495
+ snap_via_group_tol : float
496
+ Define the via group tolerance, default value is 10e-9.
497
+ properties : dict
498
+ Dictionary with key and property value.
499
+
500
+ Returns
501
+ -------
502
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileVia`
503
+ """
504
+ if isinstance(properties, dict):
505
+ self._vias.append(ControlFileVia(layer_name, properties))
506
+ return self._vias[-1]
507
+ else:
508
+ properties = {
509
+ "Name": layer_name,
510
+ "GDSDataType": str(gds_type),
511
+ "TargetLayer": target_layer,
512
+ "Material": material,
513
+ "StartLayer": start_layer,
514
+ "StopLayer": stop_layer,
515
+ "SolveInside": str(solve_inside).lower(),
516
+ "ViaGroupMethod": via_group_method,
517
+ "Persistent": via_group_persistent,
518
+ "ViaGroupTolerance": via_group_tol,
519
+ "SnapViaGroupMethod": snap_via_group_method,
520
+ "SnapViaGroupTolerance": snap_via_group_tol,
521
+ }
522
+ self._vias.append(ControlFileVia(layer_name, properties))
523
+ return self._vias[-1]
524
+
525
+ def _write_xml(self, root):
526
+ content = ET.SubElement(root, "Stackup")
527
+ content.set("schemaVersion", "1.0")
528
+ materials = ET.SubElement(content, "Materials")
529
+ for materialname, material in self.materials.items():
530
+ material._write_xml(materials)
531
+ elayers = ET.SubElement(content, "ELayers")
532
+ elayers.set("LengthUnit", self.units)
533
+ if self.metal_layer_snapping_tolerance:
534
+ elayers.set("MetalLayerSnappingTolerance", str(self.metal_layer_snapping_tolerance))
535
+ dielectrics = ET.SubElement(elayers, "Dielectrics")
536
+ dielectrics.set("BaseElevation", str(self.dielectrics_base_elevation))
537
+ # sorting dielectric layers
538
+ self._dielectrics = list(sorted(list(self._dielectrics), key=lambda x: x.properties["Index"], reverse=False))
539
+ for layer in self.dielectrics:
540
+ layer._write_xml(dielectrics)
541
+ layers = ET.SubElement(elayers, "Layers")
542
+
543
+ for layer in self.layers:
544
+ layer._write_xml(layers)
545
+ vias = ET.SubElement(elayers, "Vias")
546
+
547
+ for layer in self.vias:
548
+ layer._write_xml(vias)
549
+
550
+
551
+ class ControlFileImportOptions:
552
+ """Import Options."""
553
+
554
+ def __init__(self):
555
+ self.auto_close = False
556
+ self.convert_closed_wide_lines_to_polys = False
557
+ self.round_to = 0
558
+ self.defeature_tolerance = 0.0
559
+ self.flatten = True
560
+ self.enable_default_component_values = True
561
+ self.import_dummy_nets = False
562
+ self.gdsii_convert_polygon_to_circles = False
563
+ self.import_cross_hatch_shapes_as_lines = True
564
+ self.max_antipad_radius = 0.0
565
+ self.extracta_use_pin_names = False
566
+ self.min_bondwire_width = 0.0
567
+ self.antipad_repalce_radius = 0.0
568
+ self.gdsii_scaling_factor = 0.0
569
+ self.delte_empty_non_laminate_signal_layers = False
570
+
571
+ def _write_xml(self, root):
572
+ content = ET.SubElement(root, "ImportOptions")
573
+ content.set("AutoClose", str(self.auto_close).lower())
574
+ if self.round_to != 0:
575
+ content.set("RoundTo", str(self.round_to))
576
+ if self.defeature_tolerance != 0.0:
577
+ content.set("DefeatureTolerance", str(self.defeature_tolerance))
578
+ content.set("Flatten", str(self.flatten).lower())
579
+ content.set("EnableDefaultComponentValues", str(self.enable_default_component_values).lower())
580
+ content.set("ImportDummyNet", str(self.import_dummy_nets).lower())
581
+ content.set("GDSIIConvertPolygonToCircles", str(self.convert_closed_wide_lines_to_polys).lower())
582
+ content.set("ImportCrossHatchShapesAsLines", str(self.import_cross_hatch_shapes_as_lines).lower())
583
+ content.set("ExtractaUsePinNames", str(self.extracta_use_pin_names).lower())
584
+ if self.max_antipad_radius != 0.0:
585
+ content.set("MaxAntiPadRadius", str(self.max_antipad_radius))
586
+ if self.antipad_repalce_radius != 0.0:
587
+ content.set("AntiPadReplaceRadius", str(self.antipad_repalce_radius))
588
+ if self.min_bondwire_width != 0.0:
589
+ content.set("MinBondwireWidth", str(self.min_bondwire_width))
590
+ if self.gdsii_scaling_factor != 0.0:
591
+ content.set("GDSIIScalingFactor", str(self.gdsii_scaling_factor))
592
+ content.set("DeleteEmptyNonLaminateSignalLayers", str(self.delte_empty_non_laminate_signal_layers).lower())
593
+
594
+
595
+ class ControlExtent:
596
+ """Extent options."""
597
+
598
+ def __init__(
599
+ self,
600
+ type="bbox",
601
+ dieltype="bbox",
602
+ diel_hactor=0.25,
603
+ airbox_hfactor=0.25,
604
+ airbox_vr_p=0.25,
605
+ airbox_vr_n=0.25,
606
+ useradiation=True,
607
+ honor_primitives=True,
608
+ truncate_at_gnd=True,
609
+ ):
610
+ self.type = type
611
+ self.dieltype = dieltype
612
+ self.diel_hactor = diel_hactor
613
+ self.airbox_hfactor = airbox_hfactor
614
+ self.airbox_vr_p = airbox_vr_p
615
+ self.airbox_vr_n = airbox_vr_n
616
+ self.useradiation = useradiation
617
+ self.honor_primitives = honor_primitives
618
+ self.truncate_at_gnd = truncate_at_gnd
619
+
620
+ def _write_xml(self, root):
621
+ content = ET.SubElement(root, "Extents")
622
+ content.set("Type", self.type)
623
+ content.set("DielType", self.dieltype)
624
+ content.set("DielHorizFactor", str(self.diel_hactor))
625
+ content.set("AirboxHorizFactor", str(self.airbox_hfactor))
626
+ content.set("AirboxVertFactorPos", str(self.airbox_vr_p))
627
+ content.set("AirboxVertFactorNeg", str(self.airbox_vr_n))
628
+ content.set("UseRadiationBoundary", str(self.useradiation).lower())
629
+ content.set("DielHonorPrimitives", str(self.honor_primitives).lower())
630
+ content.set("AirboxTruncateAtGround", str(self.truncate_at_gnd).lower())
631
+
632
+
633
+ class ControlCircuitPt:
634
+ """Circuit Port."""
635
+
636
+ def __init__(self, name, x1, y1, lay1, x2, y2, lay2, z0):
637
+ self.name = name
638
+ self.x1 = x1
639
+ self.x2 = x2
640
+ self.lay1 = lay1
641
+ self.lay2 = lay2
642
+ self.y1 = y1
643
+ self.y2 = y2
644
+ self.z0 = z0
645
+
646
+ def _write_xml(self, root):
647
+ content = ET.SubElement(root, "CircuitPortPt")
648
+ content.set("Name", self.name)
649
+ content.set("x1", self.x1)
650
+ content.set("y1", self.y1)
651
+ content.set("Layer1", self.lay1)
652
+ content.set("x2", self.x2)
653
+ content.set("y2", self.y2)
654
+ content.set("Layer2", self.lay2)
655
+ content.set("Z0", self.z0)
656
+
657
+
658
+ class ControlFileComponent:
659
+ """Components."""
660
+
661
+ def __init__(self):
662
+ self.refdes = "U1"
663
+ self.partname = "BGA"
664
+ self.parttype = "IC"
665
+ self.die_type = "None"
666
+ self.die_orientation = "Chip down"
667
+ self.solderball_shape = "None"
668
+ self.solder_diameter = "65um"
669
+ self.solder_height = "65um"
670
+ self.solder_material = "solder"
671
+ self.pins = []
672
+ self.ports = []
673
+
674
+ def add_pin(self, name, x, y, layer):
675
+ self.pins.append({"Name": name, "x": x, "y": y, "Layer": layer})
676
+
677
+ def add_port(self, name, z0, pospin, refpin=None, pos_type="pin", ref_type="pin"):
678
+ args = {"Name": name, "Z0": z0}
679
+ if pos_type == "pin":
680
+ args["PosPin"] = pospin
681
+ elif pos_type == "pingroup":
682
+ args["PosPinGroup"] = pospin
683
+ if refpin:
684
+ if ref_type == "pin":
685
+ args["RefPin"] = refpin
686
+ elif ref_type == "pingroup":
687
+ args["RefPinGroup"] = refpin
688
+ elif ref_type == "net":
689
+ args["RefNet"] = refpin
690
+ self.ports.append(args)
691
+
692
+ def _write_xml(self, root):
693
+ content = ET.SubElement(root, "GDS_COMPONENT")
694
+ for p in self.pins:
695
+ prop = ET.SubElement(content, "GDS_PIN")
696
+ for pname, value in p.items():
697
+ prop.set(pname, value)
698
+
699
+ prop = ET.SubElement(content, "Component")
700
+ prop.set("RefDes", self.refdes)
701
+ prop.set("PartName", self.partname)
702
+ prop.set("PartType", self.parttype)
703
+ prop2 = ET.SubElement(prop, "DieProperties")
704
+ prop2.set("Type", self.die_type)
705
+ prop2.set("Orientation", self.die_orientation)
706
+ prop2 = ET.SubElement(prop, "SolderballProperties")
707
+ prop2.set("Shape", self.solderball_shape)
708
+ prop2.set("Diameter", self.solder_diameter)
709
+ prop2.set("Height", self.solder_height)
710
+ prop2.set("Material", self.solder_material)
711
+ for p in self.ports:
712
+ prop = ET.SubElement(prop, "ComponentPort")
713
+ for pname, value in p.items():
714
+ prop.set(pname, value)
715
+
716
+
717
+ class ControlFileComponents:
718
+ """Class for component management."""
719
+
720
+ def __init__(self):
721
+ self.units = "um"
722
+ self.components = []
723
+
724
+ def add_component(self, ref_des, partname, component_type, die_type="None", solderball_shape="None"):
725
+ """Create a new component.
726
+
727
+ Parameters
728
+ ----------
729
+ ref_des : str
730
+ Reference Designator name.
731
+ partname : str
732
+ Part name.
733
+ component_type : str
734
+ Component Type. Can be `"IC"`, `"IO"` or `"Other"`.
735
+ die_type : str, optional
736
+ Die Type. Can be `"None"`, `"Flip chip"` or `"Wire bond"`.
737
+ solderball_shape : str, optional
738
+ Solderball Type. Can be `"None"`, `"Cylinder"` or `"Spheroid"`.
739
+
740
+ Returns
741
+ -------
742
+
743
+ """
744
+ comp = ControlFileComponent()
745
+ comp.refdes = ref_des
746
+ comp.partname = partname
747
+ comp.parttype = component_type
748
+ comp.die_type = die_type
749
+ comp.solderball_shape = solderball_shape
750
+ self.components.append(comp)
751
+ return comp
752
+
753
+
754
+ class ControlFileBoundaries:
755
+ """Boundaries management."""
756
+
757
+ def __init__(self, units="um"):
758
+ self.ports = {}
759
+ self.extents = []
760
+ self.circuit_models = {}
761
+ self.circuit_elements = {}
762
+ self.units = units
763
+
764
+ def add_port(self, name, x1, y1, layer1, x2, y2, layer2, z0=50):
765
+ """Add a new port to the gds.
766
+
767
+ Parameters
768
+ ----------
769
+ name : str
770
+ Port name.
771
+ x1 : str
772
+ Pin 1 x position.
773
+ y1 : str
774
+ Pin 1 y position.
775
+ layer1 : str
776
+ Pin 1 layer.
777
+ x2 : str
778
+ Pin 2 x position.
779
+ y2 : str
780
+ Pin 2 y position.
781
+ layer2 : str
782
+ Pin 2 layer.
783
+ z0 : str
784
+ Characteristic impedance.
785
+
786
+ Returns
787
+ -------
788
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlCircuitPt`
789
+ """
790
+ self.ports[name] = ControlCircuitPt(name, str(x1), str(y1), layer1, str(x2), str(y2), layer2, str(z0))
791
+ return self.ports[name]
792
+
793
+ def add_extent(
794
+ self,
795
+ type="bbox",
796
+ dieltype="bbox",
797
+ diel_hactor=0.25,
798
+ airbox_hfactor=0.25,
799
+ airbox_vr_p=0.25,
800
+ airbox_vr_n=0.25,
801
+ useradiation=True,
802
+ honor_primitives=True,
803
+ truncate_at_gnd=True,
804
+ ):
805
+ """Add a new extent.
806
+
807
+ Parameters
808
+ ----------
809
+ type
810
+ dieltype
811
+ diel_hactor
812
+ airbox_hfactor
813
+ airbox_vr_p
814
+ airbox_vr_n
815
+ useradiation
816
+ honor_primitives
817
+ truncate_at_gnd
818
+
819
+ Returns
820
+ -------
821
+
822
+ """
823
+ self.extents.append(
824
+ ControlExtent(
825
+ type=type,
826
+ dieltype=dieltype,
827
+ diel_hactor=diel_hactor,
828
+ airbox_hfactor=airbox_hfactor,
829
+ airbox_vr_p=airbox_vr_p,
830
+ airbox_vr_n=airbox_vr_n,
831
+ useradiation=useradiation,
832
+ honor_primitives=honor_primitives,
833
+ truncate_at_gnd=truncate_at_gnd,
834
+ )
835
+ )
836
+ return self.extents[-1]
837
+
838
+ def _write_xml(self, root):
839
+ content = ET.SubElement(root, "Boundaries")
840
+ content.set("LengthUnit", self.units)
841
+ for p in self.circuit_models.values():
842
+ p._write_xml(content)
843
+ for p in self.circuit_elements.values():
844
+ p._write_xml(content)
845
+ for p in self.ports.values():
846
+ p._write_xml(content)
847
+ for p in self.extents:
848
+ p._write_xml(content)
849
+
850
+
851
+ class ControlFileSweep:
852
+ def __init__(self, name, start, stop, step, sweep_type, step_type, use_q3d):
853
+ self.name = name
854
+ self.start = start
855
+ self.stop = stop
856
+ self.step = step
857
+ self.sweep_type = sweep_type
858
+ self.step_type = step_type
859
+ self.use_q3d = use_q3d
860
+
861
+ def _write_xml(self, root):
862
+ sweep = ET.SubElement(root, "FreqSweep")
863
+ prop = ET.SubElement(sweep, "Name")
864
+ prop.text = self.name
865
+ prop = ET.SubElement(sweep, "UseQ3DForDC")
866
+ prop.text = str(self.use_q3d).lower()
867
+ prop = ET.SubElement(sweep, self.sweep_type)
868
+ prop2 = ET.SubElement(prop, self.step_type)
869
+ prop3 = ET.SubElement(prop2, "Start")
870
+ prop3.text = self.start
871
+ prop3 = ET.SubElement(prop2, "Stop")
872
+ prop3.text = self.stop
873
+ if self.step_type == "LinearStep":
874
+ prop3 = ET.SubElement(prop2, "Step")
875
+ prop3.text = str(self.step)
876
+ else:
877
+ prop3 = ET.SubElement(prop2, "Count")
878
+ prop3.text = str(self.step)
879
+
880
+
881
+ class ControlFileMeshOp:
882
+ def __init__(self, name, region, type, nets_layers):
883
+ self.name = name
884
+ self.region = name
885
+ self.type = type
886
+ self.nets_layers = nets_layers
887
+ self.num_max_elem = 1000
888
+ self.restrict_elem = False
889
+ self.restrict_length = True
890
+ self.max_length = "20um"
891
+ self.skin_depth = "1um"
892
+ self.surf_tri_length = "1mm"
893
+ self.num_layers = 2
894
+ self.region_solve_inside = False
895
+
896
+ def _write_xml(self, root):
897
+ mop = ET.SubElement(root, "MeshOperation")
898
+ prop = ET.SubElement(mop, "Name")
899
+ prop.text = self.name
900
+ prop = ET.SubElement(mop, "Enabled")
901
+ prop.text = "true"
902
+ prop = ET.SubElement(mop, "Region")
903
+ prop.text = self.region
904
+ prop = ET.SubElement(mop, "Type")
905
+ prop.text = self.type
906
+ prop = ET.SubElement(mop, "NetsLayers")
907
+ for net, layer in self.nets_layers.items():
908
+ prop2 = ET.SubElement(prop, "NetsLayer")
909
+ prop3 = ET.SubElement(prop2, "Net")
910
+ prop3.text = net
911
+ prop3 = ET.SubElement(prop2, "Layer")
912
+ prop3.text = layer
913
+ prop = ET.SubElement(mop, "RestrictElem")
914
+ prop.text = self.restrict_elem
915
+ prop = ET.SubElement(mop, "NumMaxElem")
916
+ prop.text = self.num_max_elem
917
+ if self.type == "MeshOperationLength":
918
+ prop = ET.SubElement(mop, "RestrictLength")
919
+ prop.text = self.restrict_length
920
+ prop = ET.SubElement(mop, "MaxLength")
921
+ prop.text = self.max_length
922
+ else:
923
+ prop = ET.SubElement(mop, "SkinDepth")
924
+ prop.text = self.skin_depth
925
+ prop = ET.SubElement(mop, "SurfTriLength")
926
+ prop.text = self.surf_tri_length
927
+ prop = ET.SubElement(mop, "NumLayers")
928
+ prop.text = self.num_layers
929
+ prop = ET.SubElement(mop, "RegionSolveInside")
930
+ prop.text = self.region_solve_inside
931
+
932
+
933
+ class ControlFileSetup:
934
+ """Setup Class."""
935
+
936
+ def __init__(self, name):
937
+ self.name = name
938
+ self.enabled = True
939
+ self.save_fields = False
940
+ self.save_rad_fields = False
941
+ self.frequency = "1GHz"
942
+ self.maxpasses = 10
943
+ self.max_delta = 0.02
944
+ self.union_polygons = True
945
+ self.small_voids_area = 0
946
+ self.mode_type = "IC"
947
+ self.ic_model_resolution = "Auto"
948
+ self.order_basis = "FirstOrder"
949
+ self.solver_type = "Auto"
950
+ self.low_freq_accuracy = False
951
+ self.mesh_operations = []
952
+ self.sweeps = []
953
+
954
+ def add_sweep(self, name, start, stop, step, sweep_type="Interpolating", step_type="LinearStep", use_q3d=True):
955
+ """Add a new sweep.
956
+
957
+ Parameters
958
+ ----------
959
+ name : str
960
+ Sweep name.
961
+ start : str
962
+ Frequency start.
963
+ stop : str
964
+ Frequency stop.
965
+ step : str
966
+ Frequency step or count.
967
+ sweep_type : str
968
+ Sweep type. It can be `"Discrete"` or `"Interpolating"`.
969
+ step_type : str
970
+ Sweep type. It can be `"LinearStep"`, `"DecadeCount"` or `"LinearCount"`.
971
+ use_q3d
972
+
973
+ Returns
974
+ -------
975
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileSweep`
976
+ """
977
+ self.sweeps.append(ControlFileSweep(name, start, stop, step, sweep_type, step_type, use_q3d))
978
+ return self.sweeps[-1]
979
+
980
+ def add_mesh_operation(self, name, region, type, nets_layers):
981
+ """Add mesh operations.
982
+
983
+ Parameters
984
+ ----------
985
+ name : str
986
+ Mesh name.
987
+ region : str
988
+ Region to apply mesh operation.
989
+ type : str
990
+ Mesh operation type. It can be `"MeshOperationLength"` or `"MeshOperationSkinDepth"`.
991
+ nets_layers : dict
992
+ Dictionary containing nets and layers on which apply mesh.
993
+
994
+ Returns
995
+ -------
996
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileMeshOp`
997
+
998
+ """
999
+ mop = ControlFileMeshOp(name, region, type, nets_layers)
1000
+ self.mesh_operations.append(mop)
1001
+ return mop
1002
+
1003
+ def _write_xml(self, root):
1004
+ setups = ET.SubElement(root, "HFSSSetup")
1005
+ setups.set("schemaVersion", "1.0")
1006
+ setups.set("Name", self.name)
1007
+ setup = ET.SubElement(setups, "HFSSSimulationSettings")
1008
+ prop = ET.SubElement(setup, "Enabled")
1009
+ prop.text = str(self.enabled).lower()
1010
+ prop = ET.SubElement(setup, "SaveFields")
1011
+ prop.text = str(self.save_fields).lower()
1012
+ prop = ET.SubElement(setup, "SaveRadFieldsOnly")
1013
+ prop.text = str(self.save_rad_fields).lower()
1014
+ prop = ET.SubElement(setup, "HFSSAdaptiveSettings")
1015
+ prop = ET.SubElement(prop, "AdaptiveSettings")
1016
+ prop = ET.SubElement(prop, "SingleFrequencyDataList")
1017
+ prop = ET.SubElement(prop, "AdaptiveFrequencyData")
1018
+ prop2 = ET.SubElement(prop, "AdaptiveFrequency")
1019
+ prop2.text = self.frequency
1020
+ prop2 = ET.SubElement(prop, "MaxPasses")
1021
+ prop2.text = str(self.maxpasses)
1022
+ prop2 = ET.SubElement(prop, "MaxDelta")
1023
+ prop2.text = str(self.max_delta)
1024
+ prop = ET.SubElement(setup, "HFSSDefeatureSettings")
1025
+ prop2 = ET.SubElement(prop, "UnionPolygons")
1026
+ prop2.text = str(self.union_polygons).lower()
1027
+
1028
+ prop2 = ET.SubElement(prop, "SmallVoidArea")
1029
+ prop2.text = str(self.small_voids_area)
1030
+ prop2 = ET.SubElement(prop, "ModelType")
1031
+ prop2.text = str(self.mode_type)
1032
+ prop2 = ET.SubElement(prop, "ICModelResolutionType")
1033
+ prop2.text = str(self.ic_model_resolution)
1034
+
1035
+ prop = ET.SubElement(setup, "HFSSSolverSettings")
1036
+ prop2 = ET.SubElement(prop, "OrderBasis")
1037
+ prop2.text = str(self.order_basis)
1038
+ prop2 = ET.SubElement(prop, "SolverType")
1039
+ prop2.text = str(self.solver_type)
1040
+ prop = ET.SubElement(setup, "HFSSMeshOperations")
1041
+ for mesh in self.mesh_operations:
1042
+ mesh._write_xml(prop)
1043
+ prop = ET.SubElement(setups, "HFSSSweepDataList")
1044
+ for sweep in self.sweeps:
1045
+ sweep._write_xml(prop)
1046
+
1047
+
1048
+ class ControlFileSetups:
1049
+ """Setup manager class."""
1050
+
1051
+ def __init__(self):
1052
+ self.setups = []
1053
+
1054
+ def add_setup(self, name, frequency):
1055
+ """Add a new setup
1056
+
1057
+ Parameters
1058
+ ----------
1059
+ name : str
1060
+ Setup name.
1061
+ frequency : str
1062
+ Setup Frequency.
1063
+
1064
+ Returns
1065
+ -------
1066
+ :class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileSetup`
1067
+ """
1068
+ setup = ControlFileSetup(name)
1069
+ setup.frequency = frequency
1070
+ self.setups.append(setup)
1071
+ return setup
1072
+
1073
+ def _write_xml(self, root):
1074
+ content = ET.SubElement(root, "SimulationSetups")
1075
+ for setup in self.setups:
1076
+ setup._write_xml(content)
1077
+
1078
+
1079
+ class ControlFile:
1080
+ """Control File Class. It helps the creation and modification of edb xml control files."""
1081
+
1082
+ def __init__(self, xml_input=None, tecnhology=None, layer_map=None):
1083
+ self.stackup = ControlFileStackup()
1084
+ if xml_input:
1085
+ self.parse_xml(xml_input)
1086
+ if tecnhology:
1087
+ self.parse_technology(tecnhology)
1088
+ if layer_map:
1089
+ self.parse_layer_map(layer_map)
1090
+ self.boundaries = ControlFileBoundaries()
1091
+ self.remove_holes = False
1092
+ self.remove_holes_area_minimum = 30
1093
+ self.remove_holes_units = "um"
1094
+ self.setups = ControlFileSetups()
1095
+ self.components = ControlFileComponents()
1096
+ self.import_options = ControlFileImportOptions()
1097
+ pass
1098
+
1099
+ def parse_technology(self, tecnhology, edbversion=None):
1100
+ """Parse technology files using Helic and convert it to xml file.
1101
+
1102
+ Parameters
1103
+ ----------
1104
+ layer_map : str
1105
+ Full path to technology file.
1106
+
1107
+ Returns
1108
+ -------
1109
+ bool
1110
+ """
1111
+ xml_temp = os.path.splitext(tecnhology)[0] + "_temp.xml"
1112
+ xml_temp = convert_technology_file(tech_file=tecnhology, edbversion=edbversion, control_file=xml_temp)
1113
+ if xml_temp:
1114
+ return self.parse_xml(xml_temp)
1115
+
1116
+ def parse_layer_map(self, layer_map):
1117
+ """Parse layer map and adds info to the stackup info.
1118
+ This operation must be performed after a tech file is imported.
1119
+
1120
+ Parameters
1121
+ ----------
1122
+ layer_map : str
1123
+ Full path to `".map"` file.
1124
+
1125
+ Returns
1126
+ -------
1127
+
1128
+ """
1129
+ with open(layer_map, "r") as f:
1130
+ lines = f.readlines()
1131
+ for line in lines:
1132
+ if not line.startswith("#") and re.search(r"\w+", line.strip()):
1133
+ out = re.split(r"\s+", line.strip())
1134
+ layer_name = out[0]
1135
+ layer_id = out[2]
1136
+ layer_type = out[3]
1137
+ for layer in self.stackup.layers[:]:
1138
+ if layer.name == layer_name:
1139
+ layer.properties["GDSDataType"] = layer_type
1140
+ layer.name = layer_id
1141
+ layer.properties["TargetLayer"] = layer_name
1142
+ break
1143
+ elif layer.properties.get("TargetLayer", None) == layer_name:
1144
+ new_layer = ControlFileLayer(layer_id, copy.deepcopy(layer.properties))
1145
+ new_layer.properties["GDSDataType"] = layer_type
1146
+ new_layer.name = layer_id
1147
+ new_layer.properties["TargetLayer"] = layer_name
1148
+ self.stackup.layers.append(new_layer)
1149
+ break
1150
+ for layer in self.stackup.vias[:]:
1151
+ if layer.name == layer_name:
1152
+ layer.properties["GDSDataType"] = layer_type
1153
+ layer.name = layer_id
1154
+ layer.properties["TargetLayer"] = layer_name
1155
+ break
1156
+ elif layer.properties.get("TargetLayer", None) == layer_name:
1157
+ new_layer = ControlFileVia(layer_id, copy.deepcopy(layer.properties))
1158
+ new_layer.properties["GDSDataType"] = layer_type
1159
+ new_layer.name = layer_id
1160
+ new_layer.properties["TargetLayer"] = layer_name
1161
+ self.stackup.vias.append(new_layer)
1162
+ self.stackup.vias.append(new_layer)
1163
+ break
1164
+ return True
1165
+
1166
+ def parse_xml(self, xml_input):
1167
+ """Parse an xml and populate the class with materials and Stackup only.
1168
+
1169
+ Parameters
1170
+ ----------
1171
+ xml_input : str
1172
+ Full path to xml.
1173
+
1174
+ Returns
1175
+ -------
1176
+ bool
1177
+ """
1178
+ tree = ET.parse(xml_input)
1179
+ root = tree.getroot()
1180
+ for el in root:
1181
+ if el.tag == "Stackup":
1182
+ for st_el in el:
1183
+ if st_el.tag == "Materials":
1184
+ for mat in st_el:
1185
+ mat_name = mat.attrib["Name"]
1186
+ properties = {}
1187
+ for prop in mat:
1188
+ if prop[0].tag == "Double":
1189
+ properties[prop.tag] = prop[0].text
1190
+ self.stackup.add_material(mat_name, properties)
1191
+ elif st_el.tag == "ELayers":
1192
+ if st_el.attrib == "LengthUnits":
1193
+ self.stackup.units = st_el.attrib
1194
+ for layers_el in st_el:
1195
+ if "BaseElevation" in layers_el.attrib:
1196
+ self.stackup.dielectrics_base_elevation = layers_el.attrib["BaseElevation"]
1197
+ for layer_el in layers_el:
1198
+ properties = {}
1199
+ layer_name = layer_el.attrib["Name"]
1200
+ for propname, prop_val in layer_el.attrib.items():
1201
+ properties[propname] = prop_val
1202
+ if layers_el.tag == "Dielectrics":
1203
+ self.stackup.add_dielectric(
1204
+ layer_name=layer_name,
1205
+ material=properties["Material"],
1206
+ thickness=properties["Thickness"],
1207
+ )
1208
+ elif layers_el.tag == "Layers":
1209
+ self.stackup.add_layer(layer_name=layer_name, properties=properties)
1210
+ elif layers_el.tag == "Vias":
1211
+ via = self.stackup.add_via(layer_name, properties=properties)
1212
+ for i in layer_el:
1213
+ if i.tag == "CreateViaGroups":
1214
+ via.create_via_group = True
1215
+ if "CheckContainment" in i.attrib:
1216
+ via.check_containment = (
1217
+ True if i.attrib["CheckContainment"] == "true" else False
1218
+ )
1219
+ if "Tolerance" in i.attrib:
1220
+ via.tolerance = i.attrib["Tolerance"]
1221
+ if "Method" in i.attrib:
1222
+ via.method = i.attrib["Method"]
1223
+ if "Persistent" in i.attrib:
1224
+ via.persistent = True if i.attrib["Persistent"] == "true" else False
1225
+ elif i.tag == "SnapViaGroups":
1226
+ if "Method" in i.attrib:
1227
+ via.snap_method = i.attrib["Method"]
1228
+ if "Tolerance" in i.attrib:
1229
+ via.snap_tolerance = i.attrib["Tolerance"]
1230
+ if "RemoveUnconnected" in i.attrib:
1231
+ via.remove_unconnected = (
1232
+ True if i.attrib["RemoveUnconnected"] == "true" else False
1233
+ )
1234
+ return True
1235
+
1236
+ @pyedb_function_handler()
1237
+ def write_xml(self, xml_output):
1238
+ """Write xml to output file
1239
+
1240
+ Parameters
1241
+ ----------
1242
+ xml_output : str
1243
+ Path to the output xml file.
1244
+
1245
+ Returns
1246
+ -------
1247
+ bool
1248
+ """
1249
+ control = ET.Element("{http://www.ansys.com/control}Control", attrib={"schemaVersion": "1.0"})
1250
+ self.stackup._write_xml(control)
1251
+ if self.boundaries.ports or self.boundaries.extents:
1252
+ self.boundaries._write_xml(control)
1253
+ if self.remove_holes:
1254
+ hole = ET.SubElement(control, "RemoveHoles")
1255
+ hole.set("HoleAreaMinimum", str(self.remove_holes_area_minimum))
1256
+ hole.set("LengthUnit", self.remove_holes_units)
1257
+ if self.setups.setups:
1258
+ setups = ET.SubElement(control, "SimulationSetups")
1259
+ for setup in self.setups.setups:
1260
+ setup._write_xml(setups)
1261
+ self.import_options._write_xml(control)
1262
+ if self.components.components:
1263
+ comps = ET.SubElement(control, "GDS_COMPONENTS")
1264
+ comps.set("LengthUnit", self.components.units)
1265
+ for comp in self.components.components:
1266
+ comp._write_xml(comps)
1267
+ write_pretty_xml(control, xml_output)
1268
+ return True if os.path.exists(xml_output) else False