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