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.
- pyedb/__init__.py +1 -1
- pyedb/common/nets.py +53 -139
- pyedb/configuration/cfg_common.py +1 -1
- pyedb/configuration/cfg_components.py +229 -201
- pyedb/configuration/cfg_data.py +3 -1
- pyedb/configuration/cfg_general.py +4 -2
- pyedb/configuration/cfg_modeler.py +7 -7
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +346 -290
- pyedb/configuration/cfg_ports_sources.py +243 -65
- pyedb/configuration/configuration.py +23 -3
- pyedb/dotnet/{application → database}/Variables.py +21 -21
- pyedb/dotnet/{edb_core → database}/cell/connectable.py +5 -5
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/component.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/hierarchy_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/hierarchy/model.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/layout.py +19 -19
- pyedb/dotnet/{edb_core → database}/cell/layout_obj.py +3 -3
- pyedb/dotnet/{edb_core → database}/cell/primitive/bondwire.py +1 -1
- pyedb/dotnet/{edb_core → database}/cell/primitive/path.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/primitive/primitive.py +14 -14
- pyedb/dotnet/{edb_core → database}/cell/terminal/bundle_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/edge_terminal.py +4 -4
- pyedb/dotnet/{edb_core → database}/cell/terminal/padstack_instance_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/pingroup_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/point_terminal.py +2 -2
- pyedb/dotnet/{edb_core → database}/cell/terminal/terminal.py +11 -11
- pyedb/dotnet/{edb_core → database}/cell/voltage_regulator.py +2 -2
- pyedb/dotnet/{edb_core → database}/components.py +101 -124
- pyedb/dotnet/{edb_core → database}/definition/component_def.py +5 -5
- pyedb/dotnet/{edb_core → database}/definition/component_model.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definition_obj.py +1 -1
- pyedb/dotnet/{edb_core → database}/definition/definitions.py +2 -2
- pyedb/dotnet/{edb_core → database}/definition/package_def.py +4 -4
- pyedb/dotnet/{edb_core → database}/dotnet/database.py +8 -8
- pyedb/dotnet/{edb_core → database}/dotnet/primitive.py +9 -9
- pyedb/dotnet/{edb_core → database}/edb_data/control_file.py +12 -12
- pyedb/dotnet/{edb_core → database}/edb_data/hfss_extent_info.py +7 -7
- pyedb/dotnet/{edb_core → database}/edb_data/nets_data.py +10 -13
- pyedb/dotnet/{edb_core → database}/edb_data/padstacks_data.py +60 -73
- pyedb/dotnet/{edb_core → database}/edb_data/ports.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/primitives_data.py +5 -5
- pyedb/dotnet/{edb_core → database}/edb_data/raptor_x_simulation_setup_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/simulation_configuration.py +10 -10
- pyedb/dotnet/{edb_core → database}/edb_data/sources.py +4 -4
- pyedb/dotnet/{edb_core → database}/edb_data/variables.py +1 -1
- pyedb/dotnet/{edb_core → database}/geometry/polygon_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/hfss.py +8 -8
- pyedb/dotnet/{edb_core → database}/layout_obj_instance.py +1 -1
- pyedb/dotnet/{edb_core → database}/layout_validation.py +2 -2
- pyedb/dotnet/{edb_core → database}/materials.py +23 -8
- pyedb/dotnet/{edb_core → database}/modeler.py +27 -27
- pyedb/dotnet/{edb_core → database}/net_class.py +8 -8
- pyedb/dotnet/{edb_core → database}/nets.py +12 -12
- pyedb/dotnet/{edb_core → database}/padstack.py +17 -16
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/mesh_operation.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/settings.py +18 -3
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sim_setup_info.py +2 -2
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/simulation_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
- pyedb/dotnet/{edb_core → database}/sim_setup_data/data/sweep_data.py +4 -4
- pyedb/dotnet/{edb_core → database}/siwave.py +10 -10
- pyedb/dotnet/{edb_core → database}/stackup.py +12 -12
- pyedb/dotnet/{edb_core → database}/utilities/hfss_simulation_setup.py +15 -15
- pyedb/dotnet/{edb_core → database}/utilities/obj_base.py +1 -1
- pyedb/dotnet/{edb_core → database}/utilities/simulation_setup.py +4 -3
- pyedb/dotnet/{edb_core → database}/utilities/siwave_simulation_setup.py +6 -6
- pyedb/dotnet/edb.py +118 -113
- pyedb/extensions/pre_layout_design_toolkit/via_design.py +1151 -0
- pyedb/generic/design_types.py +26 -19
- pyedb/generic/general_methods.py +1 -1
- pyedb/generic/plot.py +0 -2
- pyedb/grpc/database/__init__.py +1 -0
- pyedb/grpc/database/components.py +2354 -0
- pyedb/grpc/database/control_file.py +1277 -0
- pyedb/grpc/database/definition/component_def.py +218 -0
- pyedb/grpc/database/definition/component_model.py +39 -0
- pyedb/grpc/database/definition/component_pin.py +32 -0
- pyedb/grpc/database/definition/materials.py +1207 -0
- pyedb/grpc/database/definition/n_port_component_model.py +34 -0
- pyedb/grpc/database/definition/package_def.py +227 -0
- pyedb/grpc/database/definition/padstack_def.py +842 -0
- pyedb/grpc/database/definitions.py +70 -0
- pyedb/grpc/database/general.py +43 -0
- pyedb/grpc/database/geometry/__init__.py +0 -0
- pyedb/grpc/database/geometry/arc_data.py +93 -0
- pyedb/grpc/database/geometry/point_3d_data.py +79 -0
- pyedb/grpc/database/geometry/point_data.py +30 -0
- pyedb/grpc/database/geometry/polygon_data.py +133 -0
- pyedb/grpc/database/hfss.py +1279 -0
- pyedb/grpc/database/hierarchy/__init__.py +0 -0
- pyedb/grpc/database/hierarchy/component.py +1301 -0
- pyedb/grpc/database/hierarchy/model.py +31 -0
- pyedb/grpc/database/hierarchy/netlist_model.py +30 -0
- pyedb/grpc/database/hierarchy/pin_pair_model.py +128 -0
- pyedb/grpc/database/hierarchy/pingroup.py +245 -0
- pyedb/grpc/database/hierarchy/s_parameter_model.py +33 -0
- pyedb/grpc/database/hierarchy/spice_model.py +48 -0
- pyedb/grpc/database/layers/__init__.py +0 -0
- pyedb/grpc/database/layers/layer.py +57 -0
- pyedb/grpc/database/layers/stackup_layer.py +410 -0
- pyedb/grpc/database/layout/__init__.py +0 -0
- pyedb/grpc/database/layout/cell.py +30 -0
- pyedb/grpc/database/layout/layout.py +196 -0
- pyedb/grpc/database/layout/voltage_regulator.py +149 -0
- pyedb/grpc/database/layout_validation.py +319 -0
- pyedb/grpc/database/modeler.py +1468 -0
- pyedb/grpc/database/net/__init__.py +0 -0
- pyedb/grpc/database/net/differential_pair.py +138 -0
- pyedb/grpc/database/net/extended_net.py +340 -0
- pyedb/grpc/database/net/net.py +198 -0
- pyedb/grpc/database/net/net_class.py +93 -0
- pyedb/grpc/database/nets.py +633 -0
- pyedb/grpc/database/padstacks.py +1500 -0
- pyedb/grpc/database/ports/__init__.py +0 -0
- pyedb/grpc/database/ports/ports.py +396 -0
- pyedb/grpc/database/primitive/__init__.py +3 -0
- pyedb/grpc/database/primitive/bondwire.py +181 -0
- pyedb/grpc/database/primitive/circle.py +75 -0
- pyedb/grpc/database/primitive/padstack_instance.py +1116 -0
- pyedb/grpc/database/primitive/path.py +346 -0
- pyedb/grpc/database/primitive/polygon.py +276 -0
- pyedb/grpc/database/primitive/primitive.py +739 -0
- pyedb/grpc/database/primitive/rectangle.py +146 -0
- pyedb/grpc/database/simulation_setup/__init__.py +0 -0
- pyedb/grpc/database/simulation_setup/adaptive_frequency.py +33 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +32 -0
- pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +59 -0
- pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +35 -0
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +61 -0
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +78 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +118 -0
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +355 -0
- pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/mesh_operation.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +34 -0
- pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +33 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +64 -0
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +125 -0
- pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +34 -0
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +119 -0
- pyedb/grpc/database/simulation_setup/sweep_data.py +32 -0
- pyedb/grpc/database/siwave.py +1023 -0
- pyedb/grpc/database/source_excitations.py +2572 -0
- pyedb/grpc/database/stackup.py +2574 -0
- pyedb/grpc/database/terminal/__init__.py +0 -0
- pyedb/grpc/database/terminal/bundle_terminal.py +218 -0
- pyedb/grpc/database/terminal/edge_terminal.py +51 -0
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +171 -0
- pyedb/grpc/database/terminal/pingroup_terminal.py +162 -0
- pyedb/grpc/database/terminal/point_terminal.py +99 -0
- pyedb/grpc/database/terminal/terminal.py +470 -0
- pyedb/grpc/database/utility/__init__.py +3 -0
- pyedb/grpc/database/utility/constants.py +25 -0
- pyedb/grpc/database/utility/heat_sink.py +124 -0
- pyedb/grpc/database/utility/hfss_extent_info.py +448 -0
- pyedb/grpc/database/utility/layout_statistics.py +277 -0
- pyedb/grpc/database/utility/rlc.py +80 -0
- pyedb/grpc/database/utility/simulation_configuration.py +3305 -0
- pyedb/grpc/database/utility/sources.py +388 -0
- pyedb/grpc/database/utility/sweep_data_distribution.py +83 -0
- pyedb/grpc/database/utility/xml_control_file.py +1277 -0
- pyedb/grpc/edb.py +4151 -0
- pyedb/grpc/edb_init.py +481 -0
- pyedb/grpc/rpc_session.py +177 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +3 -2
- pyedb/ipc2581/ecad/cad_data/feature.py +4 -3
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +32 -20
- pyedb/ipc2581/ecad/cad_data/outline.py +3 -2
- pyedb/ipc2581/ecad/cad_data/package.py +4 -3
- pyedb/ipc2581/ecad/cad_data/path.py +82 -31
- pyedb/ipc2581/ecad/cad_data/polygon.py +122 -60
- pyedb/ipc2581/ecad/cad_data/profile.py +13 -12
- pyedb/ipc2581/ecad/cad_data/step.py +52 -20
- pyedb/ipc2581/ipc2581.py +47 -49
- pyedb/modeler/geometry_operators.py +1 -1
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/METADATA +9 -6
- pyedb-0.39.0.dist-info/RECORD +288 -0
- pyedb-0.37.0.dist-info/RECORD +0 -194
- /pyedb/dotnet/{edb_core → database}/__init__.py +0 -0
- /pyedb/dotnet/{application → database/cell}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell → database/cell/hierarchy}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/netlist_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/pin_pair_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/s_parameter_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/hierarchy/spice_model.py +0 -0
- /pyedb/dotnet/{edb_core → database}/cell/primitive/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/hierarchy → database/cell/terminal}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/cell/terminal → database/definition}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/definition → database/dotnet}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core/dotnet → database/edb_data}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/design_options.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/edbvalue.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/layer_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/edb_data/utilities.py +0 -0
- /pyedb/dotnet/{edb_core → database}/general.py +0 -0
- /pyedb/dotnet/{edb_core/edb_data → database/geometry}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/geometry/point_data.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/data/adaptive_frequency_data.py +0 -0
- /pyedb/dotnet/{edb_core/geometry → database/sim_setup_data/io}/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/sim_setup_data/io/siwave.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/__init__.py +0 -0
- /pyedb/dotnet/{edb_core → database}/utilities/heatsink.py +0 -0
- /pyedb/{dotnet/edb_core/sim_setup_data/io → grpc/database/definition}/__init__.py +0 -0
- {pyedb-0.37.0.dist-info → pyedb-0.39.0.dist-info}/LICENSE +0 -0
- {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
|