pyedb 0.59.0__py3-none-any.whl → 0.61.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 +23 -1
- pyedb/common/__init__.py +21 -0
- pyedb/common/nets.py +22 -0
- pyedb/component_libraries/ansys_components.py +22 -0
- pyedb/configuration/__init__.py +21 -0
- pyedb/configuration/cfg_boundaries.py +1 -1
- pyedb/configuration/cfg_common.py +1 -1
- pyedb/configuration/cfg_components.py +36 -8
- pyedb/configuration/cfg_data.py +1 -1
- pyedb/configuration/cfg_general.py +1 -1
- pyedb/configuration/cfg_modeler.py +1 -1
- pyedb/configuration/cfg_nets.py +1 -1
- pyedb/configuration/cfg_operations.py +1 -1
- pyedb/configuration/cfg_package_definition.py +1 -1
- pyedb/configuration/cfg_padstacks.py +1 -1
- pyedb/configuration/cfg_pin_groups.py +1 -1
- pyedb/configuration/cfg_ports_sources.py +3 -2
- pyedb/configuration/cfg_s_parameter_models.py +1 -1
- pyedb/configuration/cfg_setup.py +5 -1
- pyedb/configuration/cfg_spice_models.py +1 -1
- pyedb/configuration/cfg_stackup.py +1 -1
- pyedb/configuration/cfg_terminals.py +22 -0
- pyedb/configuration/configuration.py +6 -5
- pyedb/dotnet/__init__.py +21 -0
- pyedb/dotnet/clr_module.py +22 -0
- pyedb/dotnet/database/Variables.py +1 -1
- pyedb/dotnet/database/__init__.py +22 -0
- pyedb/dotnet/database/cell/__init__.py +21 -0
- pyedb/dotnet/database/cell/connectable.py +1 -1
- pyedb/dotnet/database/cell/hierarchy/__init__.py +21 -0
- pyedb/dotnet/database/cell/hierarchy/component.py +9 -7
- pyedb/dotnet/database/cell/hierarchy/hierarchy_obj.py +1 -1
- pyedb/dotnet/database/cell/hierarchy/model.py +2 -29
- pyedb/dotnet/database/cell/hierarchy/netlist_model.py +1 -1
- pyedb/dotnet/database/cell/hierarchy/pin_pair_model.py +1 -1
- pyedb/dotnet/database/cell/hierarchy/s_parameter_model.py +11 -15
- pyedb/dotnet/database/cell/hierarchy/spice_model.py +14 -8
- pyedb/dotnet/database/cell/layout.py +5 -4
- pyedb/dotnet/database/cell/layout_obj.py +1 -1
- pyedb/dotnet/database/cell/primitive/__init__.py +22 -0
- pyedb/dotnet/database/cell/primitive/bondwire.py +1 -1
- pyedb/dotnet/database/cell/primitive/path.py +1 -1
- pyedb/dotnet/database/cell/primitive/primitive.py +1 -1
- pyedb/dotnet/database/cell/terminal/__init__.py +21 -0
- pyedb/dotnet/database/cell/terminal/bundle_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/edge_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/padstack_instance_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/point_terminal.py +1 -1
- pyedb/dotnet/database/cell/terminal/terminal.py +7 -2
- pyedb/dotnet/database/cell/voltage_regulator.py +1 -1
- pyedb/dotnet/database/components.py +6 -2
- pyedb/dotnet/database/definition/__init__.py +21 -0
- pyedb/dotnet/database/definition/component_def.py +1 -1
- pyedb/dotnet/database/definition/component_model.py +1 -1
- pyedb/dotnet/database/definition/definition_obj.py +1 -1
- pyedb/dotnet/database/definition/definitions.py +1 -1
- pyedb/dotnet/database/definition/package_def.py +1 -1
- pyedb/dotnet/database/dotnet/__init__.py +21 -0
- pyedb/dotnet/database/dotnet/database.py +1 -1
- pyedb/dotnet/database/dotnet/primitive.py +1 -1
- pyedb/dotnet/database/edb_data/__init__.py +21 -0
- pyedb/dotnet/database/edb_data/control_file.py +1 -1
- pyedb/dotnet/database/edb_data/design_options.py +1 -1
- pyedb/dotnet/database/edb_data/edbvalue.py +1 -1
- pyedb/dotnet/database/edb_data/hfss_extent_info.py +1 -1
- pyedb/dotnet/database/edb_data/layer_data.py +1 -1
- pyedb/dotnet/database/edb_data/nets_data.py +1 -1
- pyedb/dotnet/database/edb_data/padstacks_data.py +6 -4
- pyedb/dotnet/database/edb_data/ports.py +1 -1
- pyedb/dotnet/database/edb_data/primitives_data.py +1 -1
- pyedb/dotnet/database/edb_data/raptor_x_simulation_setup_data.py +1 -1
- pyedb/dotnet/database/edb_data/simulation_configuration.py +1 -1
- pyedb/dotnet/database/edb_data/sources.py +1 -1
- pyedb/dotnet/database/edb_data/utilities.py +1 -1
- pyedb/dotnet/database/edb_data/variables.py +1 -1
- pyedb/dotnet/database/general.py +1 -1
- pyedb/dotnet/database/geometry/__init__.py +21 -0
- pyedb/dotnet/database/geometry/point_data.py +1 -1
- pyedb/dotnet/database/geometry/polygon_data.py +1 -1
- pyedb/dotnet/database/hfss.py +1 -1
- pyedb/dotnet/database/layout_obj_instance.py +1 -1
- pyedb/dotnet/database/layout_validation.py +1 -1
- pyedb/dotnet/database/materials.py +1 -1
- pyedb/dotnet/database/modeler.py +3 -2
- pyedb/dotnet/database/net_class.py +1 -1
- pyedb/dotnet/database/nets.py +1 -1
- pyedb/dotnet/database/padstack.py +188 -2
- pyedb/dotnet/database/sim_setup_data/__init__.py +22 -0
- pyedb/dotnet/database/sim_setup_data/data/__init__.py +22 -0
- pyedb/dotnet/database/sim_setup_data/data/adaptive_frequency_data.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/mesh_operation.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/settings.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/sim_setup_info.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/simulation_settings.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/siw_dc_ir_settings.py +1 -1
- pyedb/dotnet/database/sim_setup_data/data/sweep_data.py +1 -1
- pyedb/dotnet/database/sim_setup_data/io/__init__.py +21 -0
- pyedb/dotnet/database/sim_setup_data/io/siwave.py +1 -1
- pyedb/dotnet/database/siwave.py +1 -1
- pyedb/dotnet/database/stackup.py +1 -1
- pyedb/dotnet/database/utilities/__init__.py +22 -0
- pyedb/dotnet/database/utilities/heatsink.py +23 -0
- pyedb/dotnet/database/utilities/hfss_simulation_setup.py +1 -1
- pyedb/dotnet/database/utilities/obj_base.py +1 -1
- pyedb/dotnet/database/utilities/simulation_setup.py +1 -1
- pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +22 -0
- pyedb/dotnet/database/utilities/siwave_simulation_setup.py +22 -0
- pyedb/dotnet/database/utilities/value.py +1 -1
- pyedb/dotnet/edb.py +119 -123
- pyedb/edb_logger.py +1 -1
- pyedb/exceptions.py +22 -0
- pyedb/extensions/__init__.py +21 -0
- pyedb/extensions/create_cell_array.py +1 -1
- pyedb/extensions/via_design_backend.py +22 -0
- pyedb/generic/__init__.py +21 -0
- pyedb/generic/constants.py +1 -1
- pyedb/generic/data_handlers.py +22 -0
- pyedb/generic/design_types.py +1 -1
- pyedb/generic/filesystem.py +22 -0
- pyedb/generic/general_methods.py +22 -1
- pyedb/generic/grpc_warnings.py +22 -0
- pyedb/generic/plot.py +22 -0
- pyedb/generic/process.py +29 -2
- pyedb/generic/settings.py +1 -1
- pyedb/grpc/__init__.py +21 -0
- pyedb/grpc/database/__init__.py +21 -0
- pyedb/grpc/database/_typing.py +21 -0
- pyedb/grpc/database/components.py +9 -8
- pyedb/grpc/database/control_file.py +1 -1
- pyedb/grpc/database/definition/__init__.py +21 -0
- pyedb/grpc/database/definition/component_def.py +1 -1
- pyedb/grpc/database/definition/component_model.py +1 -1
- pyedb/grpc/database/definition/component_pin.py +1 -1
- pyedb/grpc/database/definition/materials.py +2 -2
- pyedb/grpc/database/definition/n_port_component_model.py +1 -1
- pyedb/grpc/database/definition/package_def.py +1 -1
- pyedb/grpc/database/definition/padstack_def.py +17 -10
- pyedb/grpc/database/definitions.py +1 -1
- pyedb/grpc/database/general.py +1 -1
- pyedb/grpc/database/geometry/__init__.py +21 -0
- pyedb/grpc/database/geometry/arc_data.py +1 -1
- pyedb/grpc/database/geometry/point_3d_data.py +1 -1
- pyedb/grpc/database/geometry/point_data.py +1 -1
- pyedb/grpc/database/geometry/polygon_data.py +1 -1
- pyedb/grpc/database/hfss.py +1 -1
- pyedb/grpc/database/hierarchy/__init__.py +21 -0
- pyedb/grpc/database/hierarchy/component.py +1 -1
- pyedb/grpc/database/hierarchy/model.py +1 -1
- pyedb/grpc/database/hierarchy/netlist_model.py +1 -1
- pyedb/grpc/database/hierarchy/pin_pair_model.py +1 -1
- pyedb/grpc/database/hierarchy/pingroup.py +1 -1
- pyedb/grpc/database/hierarchy/s_parameter_model.py +1 -1
- pyedb/grpc/database/hierarchy/spice_model.py +1 -1
- pyedb/grpc/database/layers/__init__.py +21 -0
- pyedb/grpc/database/layers/layer.py +22 -0
- pyedb/grpc/database/layers/stackup_layer.py +1 -1
- pyedb/grpc/database/layout/__init__.py +21 -0
- pyedb/grpc/database/layout/cell.py +1 -1
- pyedb/grpc/database/layout/layout.py +1 -1
- pyedb/grpc/database/layout/voltage_regulator.py +1 -1
- pyedb/grpc/database/layout_validation.py +1 -1
- pyedb/grpc/database/modeler.py +31 -9
- pyedb/grpc/database/net/__init__.py +21 -0
- pyedb/grpc/database/net/differential_pair.py +1 -1
- pyedb/grpc/database/net/extended_net.py +1 -1
- pyedb/grpc/database/net/net.py +1 -1
- pyedb/grpc/database/net/net_class.py +1 -1
- pyedb/grpc/database/nets.py +1 -1
- pyedb/grpc/database/padstacks.py +209 -9
- pyedb/grpc/database/ports/__init__.py +21 -0
- pyedb/grpc/database/ports/ports.py +1 -1
- pyedb/grpc/database/primitive/__init__.py +22 -0
- pyedb/grpc/database/primitive/bondwire.py +1 -1
- pyedb/grpc/database/primitive/circle.py +1 -1
- pyedb/grpc/database/primitive/padstack_instance.py +111 -16
- pyedb/grpc/database/primitive/path.py +1 -1
- pyedb/grpc/database/primitive/polygon.py +6 -4
- pyedb/grpc/database/primitive/primitive.py +1 -6
- pyedb/grpc/database/primitive/rectangle.py +1 -1
- pyedb/grpc/database/simulation_setup/__init__.py +21 -0
- pyedb/grpc/database/simulation_setup/adaptive_frequency.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_advanced_meshing_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_advanced_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_dcr_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_general_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_settings_options.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_simulation_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +1 -1
- pyedb/grpc/database/simulation_setup/hfss_solver_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/mesh_operation.py +1 -1
- pyedb/grpc/database/simulation_setup/raptor_x_advanced_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/raptor_x_general_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_settings.py +1 -1
- pyedb/grpc/database/simulation_setup/raptor_x_simulation_setup.py +1 -1
- pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +22 -0
- pyedb/grpc/database/simulation_setup/siwave_dcir_simulation_setup.py +1 -1
- pyedb/grpc/database/simulation_setup/siwave_simulation_setup.py +1 -1
- pyedb/grpc/database/simulation_setup/sweep_data.py +1 -1
- pyedb/grpc/database/siwave.py +1 -1
- pyedb/grpc/database/source_excitations.py +1 -1
- pyedb/grpc/database/stackup.py +1 -1
- pyedb/grpc/database/terminal/__init__.py +21 -0
- pyedb/grpc/database/terminal/bundle_terminal.py +1 -1
- pyedb/grpc/database/terminal/edge_terminal.py +1 -1
- pyedb/grpc/database/terminal/padstack_instance_terminal.py +1 -1
- pyedb/grpc/database/terminal/pingroup_terminal.py +1 -1
- pyedb/grpc/database/terminal/point_terminal.py +1 -1
- pyedb/grpc/database/terminal/terminal.py +1 -1
- pyedb/grpc/database/utility/__init__.py +22 -0
- pyedb/grpc/database/utility/constants.py +1 -1
- pyedb/grpc/database/utility/heat_sink.py +1 -1
- pyedb/grpc/database/utility/hfss_extent_info.py +1 -1
- pyedb/grpc/database/utility/layout_statistics.py +1 -1
- pyedb/grpc/database/utility/rlc.py +1 -1
- pyedb/grpc/database/utility/sources.py +1 -1
- pyedb/grpc/database/utility/sweep_data_distribution.py +1 -1
- pyedb/grpc/database/utility/value.py +1 -1
- pyedb/grpc/database/utility/xml_control_file.py +1 -1
- pyedb/grpc/edb.py +230 -990
- pyedb/grpc/edb_init.py +1 -1
- pyedb/grpc/rpc_session.py +17 -4
- pyedb/ipc2581/__init__.py +21 -0
- pyedb/ipc2581/bom/__init__.py +21 -0
- pyedb/ipc2581/bom/bom.py +1 -1
- pyedb/ipc2581/bom/bom_item.py +1 -1
- pyedb/ipc2581/bom/characteristics.py +1 -1
- pyedb/ipc2581/bom/refdes.py +1 -1
- pyedb/ipc2581/content/__init__.py +21 -0
- pyedb/ipc2581/content/color.py +1 -1
- pyedb/ipc2581/content/content.py +1 -1
- pyedb/ipc2581/content/dictionary_color.py +1 -1
- pyedb/ipc2581/content/dictionary_fill.py +1 -1
- pyedb/ipc2581/content/dictionary_line.py +1 -1
- pyedb/ipc2581/content/entry_color.py +1 -1
- pyedb/ipc2581/content/entry_line.py +1 -1
- pyedb/ipc2581/content/fill.py +1 -1
- pyedb/ipc2581/content/layer_ref.py +1 -1
- pyedb/ipc2581/content/standard_geometries_dictionary.py +1 -1
- pyedb/ipc2581/ecad/__init__.py +21 -0
- pyedb/ipc2581/ecad/cad_data/__init__.py +21 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +1 -1
- pyedb/ipc2581/ecad/cad_data/cad_data.py +1 -1
- pyedb/ipc2581/ecad/cad_data/component.py +1 -1
- pyedb/ipc2581/ecad/cad_data/drill.py +1 -1
- pyedb/ipc2581/ecad/cad_data/feature.py +1 -1
- pyedb/ipc2581/ecad/cad_data/layer.py +1 -1
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +1 -1
- pyedb/ipc2581/ecad/cad_data/logical_net.py +1 -1
- pyedb/ipc2581/ecad/cad_data/outline.py +1 -1
- pyedb/ipc2581/ecad/cad_data/package.py +1 -1
- pyedb/ipc2581/ecad/cad_data/padstack_def.py +1 -1
- pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +1 -1
- pyedb/ipc2581/ecad/cad_data/padstack_instance.py +1 -1
- pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +1 -1
- pyedb/ipc2581/ecad/cad_data/path.py +1 -1
- pyedb/ipc2581/ecad/cad_data/phy_net.py +1 -1
- pyedb/ipc2581/ecad/cad_data/pin.py +1 -1
- pyedb/ipc2581/ecad/cad_data/polygon.py +1 -1
- pyedb/ipc2581/ecad/cad_data/profile.py +1 -1
- pyedb/ipc2581/ecad/cad_data/stackup.py +1 -1
- pyedb/ipc2581/ecad/cad_data/stackup_group.py +1 -1
- pyedb/ipc2581/ecad/cad_data/stackup_layer.py +1 -1
- pyedb/ipc2581/ecad/cad_data/step.py +1 -1
- pyedb/ipc2581/ecad/cad_header.py +1 -1
- pyedb/ipc2581/ecad/ecad.py +1 -1
- pyedb/ipc2581/ecad/spec.py +1 -1
- pyedb/ipc2581/history_record.py +1 -1
- pyedb/ipc2581/ipc2581.py +1 -1
- pyedb/ipc2581/logistic_header.py +1 -1
- pyedb/libraries/common.py +1 -1
- pyedb/libraries/rf_libraries/base_functions.py +1 -1
- pyedb/libraries/rf_libraries/planar_antennas.py +1 -1
- pyedb/misc/__init__.py +21 -0
- pyedb/misc/aedtlib_personalib_install.py +1 -1
- pyedb/misc/decorators.py +22 -0
- pyedb/misc/downloads.py +1 -1
- pyedb/misc/misc.py +1 -1
- pyedb/misc/siw_feature_config/__init__.py +21 -0
- pyedb/misc/siw_feature_config/emc/__init__.py +21 -0
- pyedb/misc/siw_feature_config/emc/component_tags.py +22 -0
- pyedb/misc/siw_feature_config/emc/net_tags.py +22 -0
- pyedb/misc/siw_feature_config/emc/tag_library.py +22 -0
- pyedb/misc/siw_feature_config/emc/xml_generic.py +22 -0
- pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/fd_xtalk_scan_config.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/impedance_scan_config.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/net.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/pins.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py +1 -1
- pyedb/misc/siw_feature_config/xtalk_scan/td_xtalk_config.py +1 -1
- pyedb/misc/utilities.py +1 -1
- pyedb/modeler/geometry_operators.py +22 -0
- pyedb/siwave.py +22 -0
- pyedb/siwave_core/__init__.py +21 -0
- pyedb/siwave_core/cpa/__init__.py +21 -0
- pyedb/siwave_core/cpa/simulation_setup_data_model.py +22 -0
- pyedb/siwave_core/icepak.py +1 -1
- pyedb/siwave_core/product_properties.py +23 -0
- pyedb/workflow.py +22 -0
- pyedb/workflows/__init__.py +21 -0
- pyedb/workflows/job_manager/__init__.py +21 -0
- pyedb/workflows/job_manager/backend/__init__.py +21 -0
- pyedb/workflows/job_manager/backend/job_manager_handler.py +910 -0
- pyedb/workflows/job_manager/backend/job_submission.py +1169 -0
- pyedb/workflows/job_manager/backend/service.py +1663 -0
- pyedb/workflows/job_manager/backend/start_service.py +86 -0
- pyedb/workflows/job_manager/backend/submit_job_on_scheduler.py +168 -0
- pyedb/workflows/job_manager/backend/submit_local_job.py +166 -0
- pyedb/workflows/sipi/hfss_auto_configuration.py +1 -1
- pyedb/workflows/utilities/__init__.py +21 -0
- pyedb/workflows/utilities/cutout.py +1428 -0
- pyedb/workflows/utilities/hfss_log_parser.py +446 -0
- {pyedb-0.59.0.dist-info → pyedb-0.61.0.dist-info}/METADATA +7 -4
- pyedb-0.61.0.dist-info/RECORD +318 -0
- {pyedb-0.59.0.dist-info → pyedb-0.61.0.dist-info}/licenses/LICENSE +7 -7
- pyedb-0.59.0.dist-info/RECORD +0 -306
- {pyedb-0.59.0.dist-info → pyedb-0.61.0.dist-info}/WHEEL +0 -0
pyedb/grpc/edb.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2023 -
|
|
1
|
+
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
#
|
|
4
4
|
#
|
|
@@ -65,7 +65,7 @@ import sys
|
|
|
65
65
|
import tempfile
|
|
66
66
|
import time
|
|
67
67
|
import traceback
|
|
68
|
-
from typing import Dict, List, Union
|
|
68
|
+
from typing import Dict, List, Tuple, Union
|
|
69
69
|
import warnings
|
|
70
70
|
from zipfile import ZipFile as zpf
|
|
71
71
|
|
|
@@ -129,12 +129,14 @@ from pyedb.grpc.database.terminal.padstack_instance_terminal import (
|
|
|
129
129
|
PadstackInstanceTerminal,
|
|
130
130
|
)
|
|
131
131
|
from pyedb.grpc.database.terminal.terminal import Terminal
|
|
132
|
-
from pyedb.grpc.database.utility.constants import get_terminal_supported_boundary_types
|
|
133
132
|
from pyedb.grpc.database.utility.value import Value
|
|
134
133
|
from pyedb.grpc.edb_init import EdbInit
|
|
135
134
|
from pyedb.ipc2581.ipc2581 import Ipc2581
|
|
135
|
+
from pyedb.misc.decorators import deprecate_argument_name
|
|
136
136
|
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
137
137
|
from pyedb.workflow import Workflow
|
|
138
|
+
from pyedb.workflows.job_manager.backend.job_manager_handler import JobManagerHandler
|
|
139
|
+
from pyedb.workflows.utilities.cutout import Cutout
|
|
138
140
|
|
|
139
141
|
os.environ["no_proxy"] = "localhost,127.0.0.1"
|
|
140
142
|
|
|
@@ -375,6 +377,30 @@ class Edb(EdbInit):
|
|
|
375
377
|
def ansys_em_path(self):
|
|
376
378
|
return self.base_path
|
|
377
379
|
|
|
380
|
+
@staticmethod
|
|
381
|
+
def number_with_units(value, units=None):
|
|
382
|
+
"""Convert a number to a string with units. If value is a string, it's returned as is.
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
value : float, int, str
|
|
387
|
+
Input number or string.
|
|
388
|
+
units : optional
|
|
389
|
+
Units for formatting. The default is ``None``, which uses ``"meter"``.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
str
|
|
394
|
+
String concatenating the value and unit.
|
|
395
|
+
|
|
396
|
+
"""
|
|
397
|
+
if units is None:
|
|
398
|
+
units = "meter"
|
|
399
|
+
if isinstance(value, str):
|
|
400
|
+
return value
|
|
401
|
+
else:
|
|
402
|
+
return "{0}{1}".format(value, units)
|
|
403
|
+
|
|
378
404
|
def _check_remove_project_files(self, edbpath: str, remove_existing_aedt: bool) -> None:
|
|
379
405
|
aedt_file = os.path.splitext(edbpath)[0] + ".aedt"
|
|
380
406
|
files = [aedt_file, aedt_file + ".lock"]
|
|
@@ -405,6 +431,7 @@ class Edb(EdbInit):
|
|
|
405
431
|
self._source_excitation = SourceExcitation(self)
|
|
406
432
|
self._differential_pairs = DifferentialPairs(self)
|
|
407
433
|
self._extended_nets = ExtendedNets(self)
|
|
434
|
+
self._job_manager = JobManagerHandler(self)
|
|
408
435
|
|
|
409
436
|
def value(self, val) -> float:
|
|
410
437
|
"""Convert a value into a pyedb value."""
|
|
@@ -414,6 +441,17 @@ class Edb(EdbInit):
|
|
|
414
441
|
context = self.active_cell if not str(val).startswith("$") else self.active_db
|
|
415
442
|
return Value(GrpcValue(val, context), context)
|
|
416
443
|
|
|
444
|
+
@property
|
|
445
|
+
def job_manager(self):
|
|
446
|
+
"""Job manager for handling simulation tasks.
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
:class:`JobManagerHandler <pyedb.workflows.job_manager.job_manager_handler.JobManagerHandler>`
|
|
451
|
+
Job manager instance for submitting and managing simulation jobs.
|
|
452
|
+
"""
|
|
453
|
+
return self._job_manager
|
|
454
|
+
|
|
417
455
|
@property
|
|
418
456
|
def cell_names(self) -> List[str]:
|
|
419
457
|
"""List of all cell names in the database.
|
|
@@ -847,6 +885,62 @@ class Edb(EdbInit):
|
|
|
847
885
|
self.edbpath = os.path.join(working_dir, aedb_name)
|
|
848
886
|
return self.open_edb()
|
|
849
887
|
|
|
888
|
+
def import_vlctech_stackup(
|
|
889
|
+
self,
|
|
890
|
+
vlctech_file,
|
|
891
|
+
working_dir="",
|
|
892
|
+
export_xml=None,
|
|
893
|
+
):
|
|
894
|
+
"""Import a vlc.tech file and generate an ``edb.def`` file in the working directory containing only the stackup.
|
|
895
|
+
|
|
896
|
+
Parameters
|
|
897
|
+
----------
|
|
898
|
+
vlctech_file : str
|
|
899
|
+
Full path to the technology stackup file. It must be vlc.tech.
|
|
900
|
+
working_dir : str, optional
|
|
901
|
+
Directory in which to create the ``aedb`` folder. The name given to the AEDB file
|
|
902
|
+
is the same as the name of the board file.
|
|
903
|
+
export_xml : str, optional
|
|
904
|
+
Export technology file in XML control file format.
|
|
905
|
+
|
|
906
|
+
Returns
|
|
907
|
+
-------
|
|
908
|
+
Full path to the AEDB file : str
|
|
909
|
+
|
|
910
|
+
"""
|
|
911
|
+
if not working_dir:
|
|
912
|
+
working_dir = os.path.dirname(vlctech_file)
|
|
913
|
+
command = os.path.join(self.base_path, "helic", "tools", "raptorh", "bin", "make-edb")
|
|
914
|
+
if is_windows:
|
|
915
|
+
command += ".exe"
|
|
916
|
+
else:
|
|
917
|
+
os.environ["HELIC_ROOT"] = os.path.join(self.base_path, "helic")
|
|
918
|
+
cmd_make_edb = [
|
|
919
|
+
command,
|
|
920
|
+
"-t",
|
|
921
|
+
"{}".format(vlctech_file),
|
|
922
|
+
"-o",
|
|
923
|
+
"{}".format(os.path.join(working_dir, "vlctech")),
|
|
924
|
+
]
|
|
925
|
+
if export_xml:
|
|
926
|
+
cmd_make_edb.extend(["-x", "{}".format(export_xml)])
|
|
927
|
+
|
|
928
|
+
try:
|
|
929
|
+
subprocess.run(cmd_make_edb, check=True) # nosec
|
|
930
|
+
except subprocess.CalledProcessError as e: # nosec
|
|
931
|
+
raise RuntimeError(
|
|
932
|
+
"Failed to create edb. Please check if the executable is present in the base path."
|
|
933
|
+
) from e
|
|
934
|
+
|
|
935
|
+
if not os.path.exists(os.path.join(working_dir, "vlctech.aedb")):
|
|
936
|
+
self.logger.error("Failed to create edb. Please check if the executable is present in the base path.")
|
|
937
|
+
return False
|
|
938
|
+
else:
|
|
939
|
+
self.logger.info("edb successfully created.")
|
|
940
|
+
self.edbpath = os.path.join(working_dir, "vlctech.aedb")
|
|
941
|
+
self.open()
|
|
942
|
+
return self.edbpath
|
|
943
|
+
|
|
850
944
|
def export_to_ipc2581(self, ipc_path=None, units="MILLIMETER") -> str:
|
|
851
945
|
"""Export design to IPC2581 format.
|
|
852
946
|
|
|
@@ -1426,253 +1520,18 @@ class Edb(EdbInit):
|
|
|
1426
1520
|
self.edbpath = temp_inputGDS + ".aedb"
|
|
1427
1521
|
return self.open()
|
|
1428
1522
|
|
|
1429
|
-
|
|
1430
|
-
self,
|
|
1431
|
-
net_signals,
|
|
1432
|
-
extent_type,
|
|
1433
|
-
expansion_size,
|
|
1434
|
-
use_round_corner,
|
|
1435
|
-
use_pyaedt_extent=False,
|
|
1436
|
-
smart_cut=False,
|
|
1437
|
-
reference_list=[],
|
|
1438
|
-
include_pingroups=True,
|
|
1439
|
-
pins_to_preserve=None,
|
|
1440
|
-
inlcude_voids_in_extents=False,
|
|
1441
|
-
):
|
|
1442
|
-
from ansys.edb.core.geometry.polygon_data import ExtentType as GrpcExtentType
|
|
1443
|
-
|
|
1444
|
-
if extent_type in [
|
|
1445
|
-
"Conforming",
|
|
1446
|
-
"Conformal",
|
|
1447
|
-
GrpcExtentType.CONFORMING,
|
|
1448
|
-
1,
|
|
1449
|
-
]:
|
|
1450
|
-
if use_pyaedt_extent:
|
|
1451
|
-
_poly = self._create_conformal(
|
|
1452
|
-
net_signals,
|
|
1453
|
-
expansion_size,
|
|
1454
|
-
1e-12,
|
|
1455
|
-
use_round_corner,
|
|
1456
|
-
expansion_size,
|
|
1457
|
-
smart_cut,
|
|
1458
|
-
reference_list,
|
|
1459
|
-
pins_to_preserve,
|
|
1460
|
-
inlcude_voids_in_extents=inlcude_voids_in_extents,
|
|
1461
|
-
)
|
|
1462
|
-
else:
|
|
1463
|
-
_poly = self.layout.expanded_extent(
|
|
1464
|
-
net_signals,
|
|
1465
|
-
GrpcExtentType.CONFORMING,
|
|
1466
|
-
expansion_size,
|
|
1467
|
-
False,
|
|
1468
|
-
use_round_corner,
|
|
1469
|
-
1,
|
|
1470
|
-
)
|
|
1471
|
-
elif extent_type in [
|
|
1472
|
-
"Bounding",
|
|
1473
|
-
GrpcExtentType.BOUNDING_BOX,
|
|
1474
|
-
0,
|
|
1475
|
-
]:
|
|
1476
|
-
_poly = self.layout.expanded_extent(
|
|
1477
|
-
net_signals,
|
|
1478
|
-
GrpcExtentType.BOUNDING_BOX,
|
|
1479
|
-
expansion_size,
|
|
1480
|
-
False,
|
|
1481
|
-
use_round_corner,
|
|
1482
|
-
1,
|
|
1483
|
-
)
|
|
1484
|
-
else:
|
|
1485
|
-
if use_pyaedt_extent:
|
|
1486
|
-
_poly = self._create_convex_hull(
|
|
1487
|
-
net_signals,
|
|
1488
|
-
expansion_size,
|
|
1489
|
-
1e-12,
|
|
1490
|
-
use_round_corner,
|
|
1491
|
-
expansion_size,
|
|
1492
|
-
smart_cut,
|
|
1493
|
-
reference_list,
|
|
1494
|
-
pins_to_preserve,
|
|
1495
|
-
)
|
|
1496
|
-
else:
|
|
1497
|
-
_poly = self.layout.expanded_extent(
|
|
1498
|
-
net_signals,
|
|
1499
|
-
GrpcExtentType.CONFORMING,
|
|
1500
|
-
expansion_size,
|
|
1501
|
-
False,
|
|
1502
|
-
use_round_corner,
|
|
1503
|
-
1,
|
|
1504
|
-
)
|
|
1505
|
-
if not isinstance(_poly, list):
|
|
1506
|
-
_poly = [_poly]
|
|
1507
|
-
_poly = GrpcPolygonData.convex_hull(_poly)
|
|
1508
|
-
return _poly
|
|
1509
|
-
|
|
1510
|
-
def _create_conformal(
|
|
1511
|
-
self,
|
|
1512
|
-
net_signals,
|
|
1513
|
-
expansion_size,
|
|
1514
|
-
tolerance,
|
|
1515
|
-
round_corner,
|
|
1516
|
-
round_extension,
|
|
1517
|
-
smart_cutout=False,
|
|
1518
|
-
reference_list=[],
|
|
1519
|
-
pins_to_preserve=None,
|
|
1520
|
-
inlcude_voids_in_extents=False,
|
|
1521
|
-
):
|
|
1522
|
-
names = []
|
|
1523
|
-
_polys = []
|
|
1524
|
-
for net in net_signals:
|
|
1525
|
-
names.append(net.name)
|
|
1526
|
-
if pins_to_preserve:
|
|
1527
|
-
insts = self.padstacks.instances
|
|
1528
|
-
for i in pins_to_preserve:
|
|
1529
|
-
p = insts[i].position
|
|
1530
|
-
pos_1 = [i - expansion_size for i in p]
|
|
1531
|
-
pos_2 = [i + expansion_size for i in p]
|
|
1532
|
-
plane = self.modeler.Shape("rectangle", pointA=pos_1, pointB=pos_2)
|
|
1533
|
-
rectangle_data = self.modeler.shape_to_polygon_data(plane)
|
|
1534
|
-
_polys.append(rectangle_data)
|
|
1535
|
-
|
|
1536
|
-
for prim in self.modeler.primitives:
|
|
1537
|
-
if prim is not None and prim.net_name in names:
|
|
1538
|
-
_polys.append(prim)
|
|
1539
|
-
if smart_cutout:
|
|
1540
|
-
objs_data = self._smart_cut(reference_list, expansion_size)
|
|
1541
|
-
_polys.extend(objs_data)
|
|
1542
|
-
k = 0
|
|
1543
|
-
delta = expansion_size / 5
|
|
1544
|
-
while k < 10:
|
|
1545
|
-
unite_polys = []
|
|
1546
|
-
for i in _polys:
|
|
1547
|
-
if "PolygonData" not in str(i):
|
|
1548
|
-
obj_data = i.polygon_data.expand(
|
|
1549
|
-
expansion_size,
|
|
1550
|
-
round_corner,
|
|
1551
|
-
round_extension,
|
|
1552
|
-
tolerance,
|
|
1553
|
-
)
|
|
1554
|
-
else:
|
|
1555
|
-
obj_data = i.expand(
|
|
1556
|
-
expansion_size,
|
|
1557
|
-
round_corner,
|
|
1558
|
-
round_extension,
|
|
1559
|
-
tolerance,
|
|
1560
|
-
)
|
|
1561
|
-
if inlcude_voids_in_extents and "PolygonData" not in str(i) and i.has_voids and obj_data:
|
|
1562
|
-
for void in i.voids:
|
|
1563
|
-
void_data = void.polygon_data.expand(
|
|
1564
|
-
-1 * expansion_size,
|
|
1565
|
-
round_corner,
|
|
1566
|
-
round_extension,
|
|
1567
|
-
tolerance,
|
|
1568
|
-
)
|
|
1569
|
-
if void_data:
|
|
1570
|
-
for v in list(void_data):
|
|
1571
|
-
obj_data[0].holes.append(v)
|
|
1572
|
-
if obj_data:
|
|
1573
|
-
if not inlcude_voids_in_extents:
|
|
1574
|
-
unite_polys.extend(list(obj_data))
|
|
1575
|
-
else:
|
|
1576
|
-
voids_poly = []
|
|
1577
|
-
try:
|
|
1578
|
-
if i.has_voids:
|
|
1579
|
-
area = i.area()
|
|
1580
|
-
for void in i.voids:
|
|
1581
|
-
void_polydata = void.polygon_data
|
|
1582
|
-
if void_polydata.area() >= 0.05 * area:
|
|
1583
|
-
voids_poly.append(void_polydata)
|
|
1584
|
-
if voids_poly:
|
|
1585
|
-
obj_data = obj_data[0].subtract(list(obj_data), voids_poly)
|
|
1586
|
-
except Exception as e:
|
|
1587
|
-
self.logger.error(
|
|
1588
|
-
f"A(n) {type(e).__name__} error occurred in method _create_conformal of "
|
|
1589
|
-
f"class Edb at iteration {k} for data {i}: {str(e)}"
|
|
1590
|
-
)
|
|
1591
|
-
finally:
|
|
1592
|
-
unite_polys.extend(list(obj_data))
|
|
1593
|
-
_poly_unite = GrpcPolygonData.unite(unite_polys)
|
|
1594
|
-
if len(_poly_unite) == 1:
|
|
1595
|
-
self.logger.info("Correctly computed Extension at first iteration.")
|
|
1596
|
-
return _poly_unite[0]
|
|
1597
|
-
k += 1
|
|
1598
|
-
expansion_size += delta
|
|
1599
|
-
if len(_poly_unite) == 1:
|
|
1600
|
-
self.logger.info(f"Correctly computed Extension in {k} iterations.")
|
|
1601
|
-
return _poly_unite[0]
|
|
1602
|
-
else:
|
|
1603
|
-
self.logger.info("Failed to Correctly computed Extension.")
|
|
1604
|
-
areas = [i.area() for i in _poly_unite]
|
|
1605
|
-
return _poly_unite[areas.index(max(areas))]
|
|
1606
|
-
|
|
1607
|
-
def _smart_cut(self, reference_list=[], expansion_size=1e-12):
|
|
1608
|
-
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
1609
|
-
|
|
1610
|
-
_polys = []
|
|
1611
|
-
boundary_types = [
|
|
1612
|
-
"port",
|
|
1613
|
-
]
|
|
1614
|
-
terms = [term for term in self.layout.terminals if term.boundary_type in [0, 3, 4, 7, 8]]
|
|
1615
|
-
locations = []
|
|
1616
|
-
for term in terms:
|
|
1617
|
-
if term.type == "PointTerminal" and term.net.name in reference_list:
|
|
1618
|
-
pd = term.get_parameters()[1]
|
|
1619
|
-
locations.append([Value(pd.x), Value(pd.y)])
|
|
1620
|
-
for point in locations:
|
|
1621
|
-
pointA = GrpcPointData([point[0] - expansion_size, point[1] - expansion_size])
|
|
1622
|
-
pointB = GrpcPointData([point[0] + expansion_size, point[1] + expansion_size])
|
|
1623
|
-
points = [pointA, GrpcPointData([pointB.x, pointA.y]), pointB, GrpcPointData([pointA.x, pointB.y])]
|
|
1624
|
-
_polys.append(GrpcPolygonData(points=points))
|
|
1625
|
-
return _polys
|
|
1626
|
-
|
|
1627
|
-
def _create_convex_hull(
|
|
1628
|
-
self,
|
|
1629
|
-
net_signals,
|
|
1630
|
-
expansion_size,
|
|
1631
|
-
tolerance,
|
|
1632
|
-
round_corner,
|
|
1633
|
-
round_extension,
|
|
1634
|
-
smart_cut=False,
|
|
1635
|
-
reference_list=[],
|
|
1636
|
-
pins_to_preserve=None,
|
|
1637
|
-
):
|
|
1638
|
-
names = []
|
|
1639
|
-
_polys = []
|
|
1640
|
-
for net in net_signals:
|
|
1641
|
-
names.append(net.name)
|
|
1642
|
-
if pins_to_preserve:
|
|
1643
|
-
insts = self.padstacks.instances
|
|
1644
|
-
for i in pins_to_preserve:
|
|
1645
|
-
p = insts[i].position
|
|
1646
|
-
pos_1 = [i - 1e-12 for i in p]
|
|
1647
|
-
pos_2 = [i + 1e-12 for i in p]
|
|
1648
|
-
pos_3 = [pos_2[0], pos_1[1]]
|
|
1649
|
-
pos_4 = pos_1[0], pos_2[1]
|
|
1650
|
-
rectangle_data = GrpcPolygonData(points=[pos_1, pos_3, pos_2, pos_4])
|
|
1651
|
-
_polys.append(rectangle_data)
|
|
1652
|
-
for prim in self.modeler.primitives:
|
|
1653
|
-
if not prim.is_null and not prim.net.is_null:
|
|
1654
|
-
if prim.net.name in names:
|
|
1655
|
-
_polys.append(prim.polygon_data)
|
|
1656
|
-
if smart_cut:
|
|
1657
|
-
objs_data = self._smart_cut(reference_list, expansion_size)
|
|
1658
|
-
_polys.extend(objs_data)
|
|
1659
|
-
_poly = GrpcPolygonData.convex_hull(_polys)
|
|
1660
|
-
_poly = _poly.expand(
|
|
1661
|
-
offset=expansion_size, round_corner=round_corner, max_corner_ext=round_extension, tol=tolerance
|
|
1662
|
-
)[0]
|
|
1663
|
-
return _poly
|
|
1664
|
-
|
|
1523
|
+
@deprecate_argument_name({"signal_list": "signal_nets", "reference_list": "reference_nets"})
|
|
1665
1524
|
def cutout(
|
|
1666
1525
|
self,
|
|
1667
|
-
|
|
1668
|
-
|
|
1526
|
+
signal_nets=None,
|
|
1527
|
+
reference_nets=None,
|
|
1669
1528
|
extent_type="ConvexHull",
|
|
1670
1529
|
expansion_size=0.002,
|
|
1671
1530
|
use_round_corner=False,
|
|
1672
1531
|
output_aedb_path=None,
|
|
1673
1532
|
open_cutout_at_end=True,
|
|
1674
1533
|
use_pyaedt_cutout=True,
|
|
1675
|
-
number_of_threads=
|
|
1534
|
+
number_of_threads=1,
|
|
1676
1535
|
use_pyaedt_extent_computing=True,
|
|
1677
1536
|
extent_defeature=0,
|
|
1678
1537
|
remove_single_pin_components=False,
|
|
@@ -1689,757 +1548,142 @@ class Edb(EdbInit):
|
|
|
1689
1548
|
keep_lines_as_path=False,
|
|
1690
1549
|
include_voids_in_extents=False,
|
|
1691
1550
|
):
|
|
1692
|
-
"""Create
|
|
1551
|
+
"""Create a cutout using an approach entirely based on PyAEDT.
|
|
1552
|
+
This method replaces all legacy cutout methods in PyAEDT.
|
|
1553
|
+
It does in sequence:
|
|
1554
|
+
- delete all nets not in list,
|
|
1555
|
+
- create a extent of the nets,
|
|
1556
|
+
- check and delete all vias not in the extent,
|
|
1557
|
+
- check and delete all the primitives not in extent,
|
|
1558
|
+
- check and intersect all the primitives that intersect the extent.
|
|
1693
1559
|
|
|
1694
1560
|
Parameters
|
|
1695
1561
|
----------
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1562
|
+
signal_nets : list
|
|
1563
|
+
List of signal strings.
|
|
1564
|
+
reference_nets : list, optional
|
|
1565
|
+
List of references to add. The default is ``["GND"]``.
|
|
1700
1566
|
extent_type : str, optional
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1567
|
+
Type of the extension. Options are ``"Conforming"``, ``"ConvexHull"``, and
|
|
1568
|
+
``"Bounding"``. The default is ``"Conforming"``.
|
|
1569
|
+
expansion_size : float, str, optional
|
|
1570
|
+
Expansion size ratio in meters. The default is ``0.002``.
|
|
1704
1571
|
use_round_corner : bool, optional
|
|
1705
|
-
|
|
1572
|
+
Whether to use round corners. The default is ``False``.
|
|
1706
1573
|
output_aedb_path : str, optional
|
|
1707
|
-
|
|
1574
|
+
Full path and name for the new AEDB file. If None, then current aedb will be cutout.
|
|
1708
1575
|
open_cutout_at_end : bool, optional
|
|
1709
|
-
|
|
1576
|
+
Whether to open the cutout at the end. The default is ``True``.
|
|
1710
1577
|
use_pyaedt_cutout : bool, optional
|
|
1711
|
-
|
|
1578
|
+
Whether to use new PyAEDT cutout method or EDB API method.
|
|
1579
|
+
New method is faster than native API method since it benefits of multithread.
|
|
1712
1580
|
number_of_threads : int, optional
|
|
1713
|
-
|
|
1581
|
+
Number of thread to use. Default is 4. Valid only if ``use_pyaedt_cutout`` is set to ``True``.
|
|
1714
1582
|
use_pyaedt_extent_computing : bool, optional
|
|
1715
|
-
|
|
1583
|
+
Whether to use legacy extent computing (experimental) or EDB API.
|
|
1716
1584
|
extent_defeature : float, optional
|
|
1717
|
-
|
|
1585
|
+
Defeature the cutout before applying it to produce simpler geometry for mesh (Experimental).
|
|
1586
|
+
It applies only to Conforming bounding box. Default value is ``0`` which disable it.
|
|
1718
1587
|
remove_single_pin_components : bool, optional
|
|
1719
|
-
Remove
|
|
1720
|
-
custom_extent : list
|
|
1721
|
-
|
|
1722
|
-
custom_extent_units : str
|
|
1723
|
-
Units
|
|
1588
|
+
Remove all Single Pin RLC after the cutout is completed. Default is `False`.
|
|
1589
|
+
custom_extent : list
|
|
1590
|
+
Points list defining the cutout shape. This setting will override `extent_type` field.
|
|
1591
|
+
custom_extent_units : str
|
|
1592
|
+
Units of the point list. The default is ``"mm"``. Valid only if `custom_extend` is provided.
|
|
1724
1593
|
include_partial_instances : bool, optional
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1594
|
+
Whether to include padstack instances that have bounding boxes intersecting with point list polygons.
|
|
1595
|
+
This operation may slow down the cutout export.Valid only if `custom_extend` and
|
|
1596
|
+
`use_pyaedt_cutout` is provided.
|
|
1597
|
+
keep_voids : bool
|
|
1598
|
+
Boolean used for keep or not the voids intersecting the polygon used for clipping the layout.
|
|
1599
|
+
Default value is ``True``, ``False`` will remove the voids.Valid only if `custom_extend` is provided.
|
|
1728
1600
|
check_terminals : bool, optional
|
|
1729
|
-
|
|
1601
|
+
Whether to check for all reference terminals and increase extent to include them into the cutout.
|
|
1602
|
+
This applies to components which have a model (spice, touchstone or netlist) associated.
|
|
1730
1603
|
include_pingroups : bool, optional
|
|
1731
|
-
|
|
1604
|
+
Whether to check for all pingroups terminals and increase extent to include them into the cutout.
|
|
1605
|
+
It requires ``check_terminals``.
|
|
1732
1606
|
expansion_factor : int, optional
|
|
1733
|
-
|
|
1607
|
+
The method computes a float representing the largest number between
|
|
1608
|
+
the dielectric thickness or trace width multiplied by the expansion_factor factor.
|
|
1609
|
+
The trace width search is limited to nets with ports attached. Works only if `use_pyaedt_cutout`.
|
|
1610
|
+
Default is `0` to disable the search.
|
|
1734
1611
|
maximum_iterations : int, optional
|
|
1735
|
-
|
|
1612
|
+
Maximum number of iterations before stopping a search for a cutout with an error.
|
|
1613
|
+
Default is `10`.
|
|
1736
1614
|
preserve_components_with_model : bool, optional
|
|
1737
|
-
|
|
1615
|
+
Whether to preserve all pins of components that have associated models (Spice or NPort).
|
|
1616
|
+
This parameter is applicable only for a PyAEDT cutout (except point list).
|
|
1738
1617
|
simple_pad_check : bool, optional
|
|
1739
|
-
|
|
1618
|
+
Whether to use the center of the pad to find the intersection with extent or use the bounding box.
|
|
1619
|
+
Second method is much slower and requires to disable multithread on padstack removal.
|
|
1620
|
+
Default is `True`.
|
|
1740
1621
|
keep_lines_as_path : bool, optional
|
|
1741
|
-
|
|
1622
|
+
Whether to keep the lines as Path after they are cutout or convert them to PolygonData.
|
|
1623
|
+
This feature works only in Electronics Desktop (3D Layout).
|
|
1624
|
+
If the flag is set to ``True`` it can cause issues in SiWave once the Edb is imported.
|
|
1625
|
+
Default is ``False`` to generate PolygonData of cut lines.
|
|
1742
1626
|
include_voids_in_extents : bool, optional
|
|
1743
|
-
|
|
1627
|
+
Whether to compute and include voids in pyaedt extent before the cutout. Cutout time can be affected.
|
|
1628
|
+
It works only with Conforming cutout.
|
|
1629
|
+
Default is ``False`` to generate extent without voids.
|
|
1630
|
+
|
|
1744
1631
|
|
|
1745
1632
|
Returns
|
|
1746
1633
|
-------
|
|
1747
|
-
|
|
1748
|
-
|
|
1634
|
+
List
|
|
1635
|
+
List of coordinate points defining the extent used for clipping the design. If it failed return an empty
|
|
1636
|
+
list.
|
|
1749
1637
|
|
|
1750
1638
|
Examples
|
|
1751
1639
|
--------
|
|
1752
|
-
>>>
|
|
1753
|
-
>>> edb
|
|
1754
|
-
>>>
|
|
1755
|
-
>>>
|
|
1756
|
-
>>>
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
while i <= maximum_iterations:
|
|
1800
|
-
self.logger.info("-----------------------------------------")
|
|
1801
|
-
self.logger.info(f"Trying cutout with {expansion * 1e3}mm expansion size")
|
|
1802
|
-
self.logger.info("-----------------------------------------")
|
|
1803
|
-
result = self._create_cutout_multithread(
|
|
1804
|
-
signal_list=signal_list,
|
|
1805
|
-
reference_list=reference_list,
|
|
1806
|
-
extent_type=extent_type,
|
|
1807
|
-
expansion_size=expansion,
|
|
1808
|
-
use_round_corner=use_round_corner,
|
|
1809
|
-
number_of_threads=number_of_threads,
|
|
1810
|
-
custom_extent=custom_extent,
|
|
1811
|
-
output_aedb_path=dummy_path,
|
|
1812
|
-
remove_single_pin_components=remove_single_pin_components,
|
|
1813
|
-
use_pyaedt_extent_computing=use_pyaedt_extent_computing,
|
|
1814
|
-
extent_defeature=extent_defeature,
|
|
1815
|
-
custom_extent_units=custom_extent_units,
|
|
1816
|
-
check_terminals=check_terminals,
|
|
1817
|
-
include_pingroups=include_pingroups,
|
|
1818
|
-
preserve_components_with_model=preserve_components_with_model,
|
|
1819
|
-
include_partial=include_partial_instances,
|
|
1820
|
-
simple_pad_check=simple_pad_check,
|
|
1821
|
-
keep_lines_as_path=keep_lines_as_path,
|
|
1822
|
-
inlcude_voids_in_extents=include_voids_in_extents,
|
|
1823
|
-
)
|
|
1824
|
-
if self.are_port_reference_terminals_connected():
|
|
1825
|
-
if output_aedb_path:
|
|
1826
|
-
self.save_edb_as(output_aedb_path)
|
|
1827
|
-
else:
|
|
1828
|
-
self.save_edb_as(legacy_path)
|
|
1829
|
-
working_cutout = True
|
|
1830
|
-
break
|
|
1831
|
-
self.close_edb()
|
|
1832
|
-
self.edbpath = legacy_path
|
|
1833
|
-
self.open()
|
|
1834
|
-
i += 1
|
|
1835
|
-
expansion = expansion_size * i
|
|
1836
|
-
if working_cutout:
|
|
1837
|
-
msg = f"Cutout completed in {i} iterations with expansion size of {expansion * 1e3}mm"
|
|
1838
|
-
self.logger.info_timer(msg, start)
|
|
1839
|
-
else:
|
|
1840
|
-
msg = f"Cutout failed after {i} iterations and expansion size of {expansion * 1e3}mm"
|
|
1841
|
-
self.logger.info_timer(msg, start)
|
|
1842
|
-
return False
|
|
1843
|
-
else:
|
|
1844
|
-
result = self._create_cutout_multithread(
|
|
1845
|
-
signal_list=signal_list,
|
|
1846
|
-
reference_list=reference_list,
|
|
1847
|
-
extent_type=extent_type,
|
|
1848
|
-
expansion_size=expansion_size,
|
|
1849
|
-
use_round_corner=use_round_corner,
|
|
1850
|
-
number_of_threads=number_of_threads,
|
|
1851
|
-
custom_extent=custom_extent,
|
|
1852
|
-
output_aedb_path=output_aedb_path,
|
|
1853
|
-
remove_single_pin_components=remove_single_pin_components,
|
|
1854
|
-
use_pyaedt_extent_computing=use_pyaedt_extent_computing,
|
|
1855
|
-
extent_defeature=extent_defeature,
|
|
1856
|
-
custom_extent_units=custom_extent_units,
|
|
1857
|
-
check_terminals=check_terminals,
|
|
1858
|
-
include_pingroups=include_pingroups,
|
|
1859
|
-
preserve_components_with_model=preserve_components_with_model,
|
|
1860
|
-
include_partial=include_partial_instances,
|
|
1861
|
-
simple_pad_check=simple_pad_check,
|
|
1862
|
-
keep_lines_as_path=keep_lines_as_path,
|
|
1863
|
-
inlcude_voids_in_extents=include_voids_in_extents,
|
|
1864
|
-
)
|
|
1865
|
-
if result and not open_cutout_at_end and self.edbpath != legacy_path:
|
|
1866
|
-
self.save_edb()
|
|
1867
|
-
self.close_edb()
|
|
1868
|
-
self.edbpath = legacy_path
|
|
1869
|
-
self.open_edb()
|
|
1870
|
-
return result
|
|
1871
|
-
|
|
1872
|
-
def _create_cutout_legacy(
|
|
1873
|
-
self,
|
|
1874
|
-
signal_list=[],
|
|
1875
|
-
reference_list=["GND"],
|
|
1876
|
-
extent_type="Conforming",
|
|
1877
|
-
expansion_size=0.002,
|
|
1878
|
-
use_round_corner=False,
|
|
1879
|
-
output_aedb_path=None,
|
|
1880
|
-
open_cutout_at_end=True,
|
|
1881
|
-
use_pyaedt_extent_computing=False,
|
|
1882
|
-
remove_single_pin_components=False,
|
|
1883
|
-
check_terminals=False,
|
|
1884
|
-
include_pingroups=True,
|
|
1885
|
-
inlcude_voids_in_extents=False,
|
|
1886
|
-
):
|
|
1887
|
-
expansion_size = Value(expansion_size)
|
|
1888
|
-
|
|
1889
|
-
# validate nets in layout
|
|
1890
|
-
net_signals = [net for net in self.layout.nets if net.name in signal_list]
|
|
1891
|
-
|
|
1892
|
-
# validate references in layout
|
|
1893
|
-
_netsClip = [net for net in self.layout.nets if net.name in reference_list]
|
|
1894
|
-
|
|
1895
|
-
_poly = self._create_extent(
|
|
1896
|
-
net_signals,
|
|
1897
|
-
extent_type,
|
|
1898
|
-
expansion_size,
|
|
1899
|
-
use_round_corner,
|
|
1900
|
-
use_pyaedt_extent_computing,
|
|
1901
|
-
smart_cut=check_terminals,
|
|
1902
|
-
reference_list=reference_list,
|
|
1903
|
-
include_pingroups=include_pingroups,
|
|
1904
|
-
inlcude_voids_in_extents=inlcude_voids_in_extents,
|
|
1905
|
-
)
|
|
1906
|
-
_poly1 = GrpcPolygonData(arcs=_poly.arc_data, closed=True)
|
|
1907
|
-
if inlcude_voids_in_extents:
|
|
1908
|
-
for hole in _poly.holes:
|
|
1909
|
-
if hole.area() >= 0.05 * _poly1.area():
|
|
1910
|
-
_poly1.holes.append(hole)
|
|
1911
|
-
_poly = _poly1
|
|
1912
|
-
# Create new cutout cell/design
|
|
1913
|
-
included_nets_list = signal_list + reference_list
|
|
1914
|
-
included_nets = [net for net in self.layout.nets if net.name in included_nets_list]
|
|
1915
|
-
_cutout = self.active_cell.cutout(included_nets, _netsClip, _poly, True)
|
|
1916
|
-
# _cutout.simulation_setups = self.active_cell.simulation_setups see bug #433 status.
|
|
1917
|
-
_dbCells = [_cutout]
|
|
1918
|
-
if output_aedb_path:
|
|
1919
|
-
from ansys.edb.core.database import Database as GrpcDatabase
|
|
1920
|
-
|
|
1921
|
-
db2 = GrpcDatabase.create(output_aedb_path)
|
|
1922
|
-
db2.copy_cells(_dbCells) # Copies cutout cell/design to db2 project
|
|
1923
|
-
if len(list(db2.top_circuit_cells)) > 0:
|
|
1924
|
-
for net in db2.top_circuit_cells[0].layout.nets:
|
|
1925
|
-
if not net.name in included_nets_list:
|
|
1926
|
-
net.delete()
|
|
1927
|
-
db2.save()
|
|
1928
|
-
for c in self.active_db.top_circuit_cells:
|
|
1929
|
-
if c.name == _cutout.name:
|
|
1930
|
-
c.delete()
|
|
1931
|
-
if open_cutout_at_end: # pragma: no cover
|
|
1932
|
-
self._db = db2
|
|
1933
|
-
self.edbpath = output_aedb_path
|
|
1934
|
-
self._active_cell = self.top_circuit_cells[0]
|
|
1935
|
-
self.edbpath = self.directory
|
|
1936
|
-
self._init_objects()
|
|
1937
|
-
if remove_single_pin_components:
|
|
1938
|
-
self.components.delete_single_pin_rlc()
|
|
1939
|
-
self.logger.info_timer("Single Pins components deleted")
|
|
1940
|
-
self.components.refresh_components()
|
|
1941
|
-
else:
|
|
1942
|
-
if remove_single_pin_components:
|
|
1943
|
-
try:
|
|
1944
|
-
from ansys.edb.core.hierarchy.component_group import (
|
|
1945
|
-
ComponentGroup as GrpcComponentGroup,
|
|
1946
|
-
)
|
|
1947
|
-
|
|
1948
|
-
layout = db2.circuit_cells[0].layout
|
|
1949
|
-
_cmps = [l for l in layout.groups if isinstance(l, GrpcComponentGroup) and l.num_pins < 2]
|
|
1950
|
-
for _cmp in _cmps:
|
|
1951
|
-
_cmp.delete()
|
|
1952
|
-
except:
|
|
1953
|
-
self.logger.error("Failed to remove single pin components.")
|
|
1954
|
-
db2.close()
|
|
1955
|
-
source = os.path.join(output_aedb_path, "edb.def.tmp")
|
|
1956
|
-
target = os.path.join(output_aedb_path, "edb.def")
|
|
1957
|
-
self._wait_for_file_release(file_to_release=output_aedb_path)
|
|
1958
|
-
if os.path.exists(source) and not os.path.exists(target):
|
|
1959
|
-
try:
|
|
1960
|
-
shutil.copy(source, target)
|
|
1961
|
-
except Exception as e:
|
|
1962
|
-
self.logger.error(f"Failed to copy {source} to {target} - {type(e).__name__}: {str(e)}")
|
|
1963
|
-
elif open_cutout_at_end:
|
|
1964
|
-
self._active_cell = _cutout
|
|
1965
|
-
self._init_objects()
|
|
1966
|
-
if remove_single_pin_components:
|
|
1967
|
-
self.components.delete_single_pin_rlc()
|
|
1968
|
-
self.logger.info_timer("Single Pins components deleted")
|
|
1969
|
-
self.components.refresh_components()
|
|
1970
|
-
return [[Value(pt.x), Value(pt.y)] for pt in _poly.without_arcs().points]
|
|
1971
|
-
|
|
1972
|
-
def _create_cutout_multithread(
|
|
1973
|
-
self,
|
|
1974
|
-
signal_list=[],
|
|
1975
|
-
reference_list=["GND"],
|
|
1976
|
-
extent_type="Conforming",
|
|
1977
|
-
expansion_size=0.002,
|
|
1978
|
-
use_round_corner=False,
|
|
1979
|
-
number_of_threads=4,
|
|
1980
|
-
custom_extent=None,
|
|
1981
|
-
output_aedb_path=None,
|
|
1982
|
-
remove_single_pin_components=False,
|
|
1983
|
-
use_pyaedt_extent_computing=False,
|
|
1984
|
-
extent_defeature=0.0,
|
|
1985
|
-
custom_extent_units="mm",
|
|
1986
|
-
check_terminals=False,
|
|
1987
|
-
include_pingroups=True,
|
|
1988
|
-
preserve_components_with_model=False,
|
|
1989
|
-
include_partial=False,
|
|
1990
|
-
simple_pad_check=True,
|
|
1991
|
-
keep_lines_as_path=False,
|
|
1992
|
-
inlcude_voids_in_extents=False,
|
|
1993
|
-
):
|
|
1994
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
1995
|
-
|
|
1996
|
-
if output_aedb_path:
|
|
1997
|
-
self.save_edb_as(output_aedb_path)
|
|
1998
|
-
self.logger.info("Cutout Multithread started.")
|
|
1999
|
-
expansion_size = Value(expansion_size)
|
|
2000
|
-
|
|
2001
|
-
timer_start = self.logger.reset_timer()
|
|
2002
|
-
if custom_extent:
|
|
2003
|
-
if not reference_list and not signal_list:
|
|
2004
|
-
reference_list = self.nets.netlist[::]
|
|
2005
|
-
all_list = reference_list
|
|
2006
|
-
else:
|
|
2007
|
-
reference_list = reference_list + signal_list
|
|
2008
|
-
all_list = reference_list
|
|
2009
|
-
else:
|
|
2010
|
-
all_list = signal_list + reference_list
|
|
2011
|
-
pins_to_preserve = []
|
|
2012
|
-
nets_to_preserve = []
|
|
2013
|
-
if preserve_components_with_model:
|
|
2014
|
-
for el in self.components.instances.values():
|
|
2015
|
-
if el.model_type in [
|
|
2016
|
-
"SPICEModel",
|
|
2017
|
-
"SParameterModel",
|
|
2018
|
-
"NetlistModel",
|
|
2019
|
-
] and list(set(el.nets[:]) & set(signal_list[:])):
|
|
2020
|
-
pins_to_preserve.extend([i.edb_uid for i in el.pins.values()])
|
|
2021
|
-
nets_to_preserve.extend(el.nets)
|
|
2022
|
-
if include_pingroups:
|
|
2023
|
-
for pingroup in self.layout.pin_groups:
|
|
2024
|
-
for pin_name, pin in pingroup.pins.items():
|
|
2025
|
-
if pin_name in reference_list:
|
|
2026
|
-
pins_to_preserve.append(pin.edb_uid)
|
|
2027
|
-
if check_terminals:
|
|
2028
|
-
terms = [
|
|
2029
|
-
term for term in self.layout.terminals if term.boundary_type in get_terminal_supported_boundary_types()
|
|
2030
|
-
]
|
|
2031
|
-
for term in terms:
|
|
2032
|
-
if isinstance(term, PadstackInstanceTerminal):
|
|
2033
|
-
if term.net.name in reference_list:
|
|
2034
|
-
pins_to_preserve.append(term.edb_uid)
|
|
2035
|
-
delete_list = []
|
|
2036
|
-
|
|
2037
|
-
for i in self.nets.nets.values():
|
|
2038
|
-
name = i.name
|
|
2039
|
-
if name not in all_list and name not in nets_to_preserve:
|
|
2040
|
-
delete_list.append(i)
|
|
2041
|
-
# i.delete()
|
|
2042
|
-
for i in delete_list:
|
|
2043
|
-
i.delete()
|
|
2044
|
-
reference_pinsts = []
|
|
2045
|
-
reference_prims = []
|
|
2046
|
-
reference_paths = []
|
|
2047
|
-
delete_list = []
|
|
2048
|
-
for i in self.padstacks.instances.values():
|
|
2049
|
-
net_name = i.net_name
|
|
2050
|
-
id = i.id
|
|
2051
|
-
if net_name not in all_list and id not in pins_to_preserve:
|
|
2052
|
-
delete_list.append(i)
|
|
2053
|
-
# i.delete()
|
|
2054
|
-
elif net_name in reference_list and id not in pins_to_preserve:
|
|
2055
|
-
reference_pinsts.append(i)
|
|
2056
|
-
for i in self.modeler.primitives:
|
|
2057
|
-
if not i.is_null and not i.net.is_null:
|
|
2058
|
-
if i.net.name not in all_list:
|
|
2059
|
-
# i.delete()
|
|
2060
|
-
delete_list.append(i)
|
|
2061
|
-
elif i.net.name in reference_list and not i.is_void:
|
|
2062
|
-
if keep_lines_as_path and isinstance(i, Path):
|
|
2063
|
-
reference_paths.append(i)
|
|
2064
|
-
else:
|
|
2065
|
-
reference_prims.append(i)
|
|
2066
|
-
self.logger.info_timer("Net clean up")
|
|
2067
|
-
self.logger.reset_timer()
|
|
2068
|
-
for i in delete_list:
|
|
2069
|
-
i.delete()
|
|
2070
|
-
if custom_extent and isinstance(custom_extent, list):
|
|
2071
|
-
if custom_extent[0] != custom_extent[-1]:
|
|
2072
|
-
custom_extent.append(custom_extent[0])
|
|
2073
|
-
custom_extent = [
|
|
2074
|
-
[
|
|
2075
|
-
self.number_with_units(i[0], custom_extent_units),
|
|
2076
|
-
self.number_with_units(i[1], custom_extent_units),
|
|
2077
|
-
]
|
|
2078
|
-
for i in custom_extent
|
|
2079
|
-
]
|
|
2080
|
-
_poly = GrpcPolygonData(points=custom_extent)
|
|
2081
|
-
elif custom_extent:
|
|
2082
|
-
_poly = custom_extent
|
|
2083
|
-
else:
|
|
2084
|
-
net_signals = [net for net in self.layout.nets if net.name in signal_list]
|
|
2085
|
-
_poly = self._create_extent(
|
|
2086
|
-
net_signals,
|
|
2087
|
-
extent_type,
|
|
2088
|
-
expansion_size,
|
|
2089
|
-
use_round_corner,
|
|
2090
|
-
use_pyaedt_extent_computing,
|
|
2091
|
-
smart_cut=check_terminals,
|
|
2092
|
-
reference_list=reference_list,
|
|
2093
|
-
include_pingroups=include_pingroups,
|
|
2094
|
-
pins_to_preserve=pins_to_preserve,
|
|
2095
|
-
inlcude_voids_in_extents=inlcude_voids_in_extents,
|
|
2096
|
-
)
|
|
2097
|
-
from ansys.edb.core.geometry.polygon_data import (
|
|
2098
|
-
ExtentType as GrpcExtentType,
|
|
2099
|
-
)
|
|
2100
|
-
|
|
2101
|
-
if extent_type in ["Conformal", "Conforming", GrpcExtentType.CONFORMING, 1]:
|
|
2102
|
-
if extent_defeature > 0:
|
|
2103
|
-
_poly = _poly.defeature(extent_defeature)
|
|
2104
|
-
_poly1 = GrpcPolygonData(arcs=_poly.arc_data, closed=True)
|
|
2105
|
-
if inlcude_voids_in_extents:
|
|
2106
|
-
for hole in list(_poly.holes):
|
|
2107
|
-
if hole.area() >= 0.05 * _poly1.area():
|
|
2108
|
-
_poly1.holes.append(hole)
|
|
2109
|
-
self.logger.info(f"Number of voids included:{len(list(_poly1.holes))}")
|
|
2110
|
-
_poly = _poly1
|
|
2111
|
-
if not _poly.points:
|
|
2112
|
-
self._logger.error("Failed to create Extent.")
|
|
2113
|
-
return []
|
|
2114
|
-
self.logger.info_timer("Expanded Net Polygon Creation")
|
|
2115
|
-
self.logger.reset_timer()
|
|
2116
|
-
_poly_list = [_poly]
|
|
2117
|
-
prims_to_delete = []
|
|
2118
|
-
poly_to_create = []
|
|
2119
|
-
pins_to_delete = []
|
|
2120
|
-
|
|
2121
|
-
def intersect(poly1, poly2):
|
|
2122
|
-
if not isinstance(poly2, list):
|
|
2123
|
-
poly2 = [poly2]
|
|
2124
|
-
return poly1.intersect(poly1, poly2)
|
|
2125
|
-
|
|
2126
|
-
def subtract(poly, voids):
|
|
2127
|
-
return poly.subtract(poly, voids)
|
|
2128
|
-
|
|
2129
|
-
def clip_path(path):
|
|
2130
|
-
pdata = path.polygon_data
|
|
2131
|
-
int_data = _poly.intersection_type(pdata)
|
|
2132
|
-
if int_data == 0:
|
|
2133
|
-
prims_to_delete.append(path)
|
|
2134
|
-
return
|
|
2135
|
-
result = path.set_clip_info(_poly, True)
|
|
2136
|
-
if not result:
|
|
2137
|
-
self.logger.info(f"Failed to clip path {path.id}. Clipping as polygon.")
|
|
2138
|
-
reference_prims.append(path)
|
|
2139
|
-
|
|
2140
|
-
def clean_prim(prim_1): # pragma: no cover
|
|
2141
|
-
pdata = prim_1.polygon_data
|
|
2142
|
-
int_data = _poly.intersection_type(pdata)
|
|
2143
|
-
if int_data == 2:
|
|
2144
|
-
if not inlcude_voids_in_extents:
|
|
2145
|
-
return
|
|
2146
|
-
skip = False
|
|
2147
|
-
for hole in list(_poly.Holes):
|
|
2148
|
-
if hole.intersection_type(pdata) == 0:
|
|
2149
|
-
prims_to_delete.append(prim_1)
|
|
2150
|
-
return
|
|
2151
|
-
elif hole.intersection_type(pdata) == 1:
|
|
2152
|
-
skip = True
|
|
2153
|
-
if skip:
|
|
2154
|
-
return
|
|
2155
|
-
elif int_data == 0:
|
|
2156
|
-
prims_to_delete.append(prim_1)
|
|
2157
|
-
return
|
|
2158
|
-
list_poly = intersect(_poly, pdata)
|
|
2159
|
-
if list_poly:
|
|
2160
|
-
net = prim_1.net.name
|
|
2161
|
-
voids = prim_1.voids
|
|
2162
|
-
for p in list_poly:
|
|
2163
|
-
if not p.points:
|
|
2164
|
-
continue
|
|
2165
|
-
list_void = []
|
|
2166
|
-
if voids:
|
|
2167
|
-
voids_data = [void.polygon_data for void in voids]
|
|
2168
|
-
list_prims = subtract(p, voids_data)
|
|
2169
|
-
for prim in list_prims:
|
|
2170
|
-
if prim.points:
|
|
2171
|
-
poly_to_create.append([prim, prim_1.layer.name, net, list_void])
|
|
2172
|
-
else:
|
|
2173
|
-
poly_to_create.append([p, prim_1.layer.name, net, list_void])
|
|
2174
|
-
|
|
2175
|
-
prims_to_delete.append(prim_1)
|
|
2176
|
-
|
|
2177
|
-
def pins_clean(pinst):
|
|
2178
|
-
if not pinst.in_polygon(_poly, include_partial=include_partial, simple_check=simple_pad_check):
|
|
2179
|
-
pins_to_delete.append(pinst)
|
|
2180
|
-
|
|
2181
|
-
if not simple_pad_check:
|
|
2182
|
-
pad_cores = 1
|
|
2183
|
-
else:
|
|
2184
|
-
pad_cores = number_of_threads
|
|
2185
|
-
with ThreadPoolExecutor(pad_cores) as pool:
|
|
2186
|
-
pool.map(lambda item: pins_clean(item), reference_pinsts)
|
|
2187
|
-
|
|
2188
|
-
for pin in pins_to_delete:
|
|
2189
|
-
pin.delete()
|
|
2190
|
-
|
|
2191
|
-
self.logger.info_timer(f"Padstack Instances removal completed. {len(pins_to_delete)} instances removed.")
|
|
2192
|
-
self.logger.reset_timer()
|
|
2193
|
-
|
|
2194
|
-
for item in reference_paths:
|
|
2195
|
-
clip_path(item)
|
|
2196
|
-
for prim in reference_prims: # removing multithreading as failing with new layer from primitive
|
|
2197
|
-
clean_prim(prim)
|
|
2198
|
-
|
|
2199
|
-
for el in poly_to_create:
|
|
2200
|
-
self.modeler.create_polygon(el[0], el[1], net_name=el[2], voids=el[3])
|
|
2201
|
-
|
|
2202
|
-
for prim in prims_to_delete:
|
|
2203
|
-
prim.delete()
|
|
2204
|
-
|
|
2205
|
-
self.logger.info_timer(f"Primitives cleanup completed. {len(prims_to_delete)} primitives deleted.")
|
|
2206
|
-
self.logger.reset_timer()
|
|
2207
|
-
|
|
2208
|
-
i = 0
|
|
2209
|
-
for _, val in self.components.instances.items():
|
|
2210
|
-
if val.numpins == 0:
|
|
2211
|
-
val.delete()
|
|
2212
|
-
i += 1
|
|
2213
|
-
i += 1
|
|
2214
|
-
self.logger.info(f"Deleted {i} additional components")
|
|
2215
|
-
if remove_single_pin_components:
|
|
2216
|
-
self.components.delete_single_pin_rlc()
|
|
2217
|
-
self.logger.info_timer("Single Pins components deleted")
|
|
2218
|
-
|
|
2219
|
-
self.components.refresh_components()
|
|
2220
|
-
if output_aedb_path:
|
|
2221
|
-
self.save_edb()
|
|
2222
|
-
self.logger.info_timer("Cutout completed.", timer_start)
|
|
2223
|
-
self.logger.reset_timer()
|
|
2224
|
-
return [[Value(pt.x), Value(pt.y)] for pt in _poly.without_arcs().points]
|
|
2225
|
-
|
|
2226
|
-
def get_conformal_polygon_from_netlist(self, netlist=None) -> Union[bool, Polygon]:
|
|
2227
|
-
"""Returns conformal polygon data based on a netlist.
|
|
2228
|
-
|
|
2229
|
-
Parameters
|
|
2230
|
-
----------
|
|
2231
|
-
netlist : List of net names.
|
|
2232
|
-
list[str]
|
|
2233
|
-
|
|
2234
|
-
Returns
|
|
2235
|
-
-------
|
|
2236
|
-
:class:`PolygonData <ansys.edb.core.geometry.polygon_data.PolygonData>`
|
|
2237
|
-
"""
|
|
2238
|
-
from ansys.edb.core.geometry.polygon_data import ExtentType as GrpcExtentType
|
|
2239
|
-
|
|
2240
|
-
temp_edb_path = self.edbpath[:-5] + "_temp_aedb.aedb"
|
|
2241
|
-
shutil.copytree(self.edbpath, temp_edb_path)
|
|
2242
|
-
temp_edb = Edb(temp_edb_path)
|
|
2243
|
-
for via in list(temp_edb.padstacks.instances.values()):
|
|
2244
|
-
via.pin.delete()
|
|
2245
|
-
if netlist:
|
|
2246
|
-
nets = [net for net in temp_edb.layout.nets if net.name in netlist]
|
|
2247
|
-
_poly = temp_edb.layout.expanded_extent(nets, GrpcExtentType.CONFORMING, 0.0, True, True, 1)
|
|
2248
|
-
else:
|
|
2249
|
-
nets = [net for net in temp_edb.layout.nets if "gnd" in net.name.lower()]
|
|
2250
|
-
_poly = temp_edb.layout.expanded_extent(nets, GrpcExtentType.CONFORMING, 0.0, True, True, 1)
|
|
2251
|
-
temp_edb.close()
|
|
2252
|
-
if _poly:
|
|
2253
|
-
return _poly
|
|
2254
|
-
else:
|
|
2255
|
-
return False
|
|
2256
|
-
|
|
2257
|
-
def number_with_units(self, value, units=None) -> str:
|
|
2258
|
-
"""Convert a number to a string with units. If value is a string, it's returned as is.
|
|
2259
|
-
|
|
2260
|
-
Parameters
|
|
2261
|
-
----------
|
|
2262
|
-
value : float, int, str
|
|
2263
|
-
Input number or string.
|
|
2264
|
-
units : optional
|
|
2265
|
-
Units for formatting. The default is ``None``, which uses ``"meter"``.
|
|
2266
|
-
|
|
2267
|
-
Returns
|
|
2268
|
-
-------
|
|
2269
|
-
str
|
|
2270
|
-
String concatenating the value and unit.
|
|
2271
|
-
|
|
2272
|
-
"""
|
|
2273
|
-
if units is None:
|
|
2274
|
-
units = "meter"
|
|
2275
|
-
if isinstance(value, str):
|
|
2276
|
-
return value
|
|
2277
|
-
else:
|
|
2278
|
-
return f"{value}{units}"
|
|
2279
|
-
|
|
2280
|
-
def _create_cutout_on_point_list(
|
|
2281
|
-
self,
|
|
2282
|
-
point_list,
|
|
2283
|
-
units="mm",
|
|
2284
|
-
output_aedb_path=None,
|
|
2285
|
-
open_cutout_at_end=True,
|
|
2286
|
-
nets_to_include=None,
|
|
2287
|
-
include_partial_instances=False,
|
|
2288
|
-
keep_voids=True,
|
|
2289
|
-
):
|
|
2290
|
-
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
2291
|
-
|
|
2292
|
-
if point_list[0] != point_list[-1]:
|
|
2293
|
-
point_list.append(point_list[0])
|
|
2294
|
-
point_list = [[self.number_with_units(i[0], units), self.number_with_units(i[1], units)] for i in point_list]
|
|
2295
|
-
polygon_data = GrpcPolygonData(points=[GrpcPointData(pt) for pt in point_list])
|
|
2296
|
-
_ref_nets = []
|
|
2297
|
-
if nets_to_include:
|
|
2298
|
-
self.logger.info(f"Creating cutout on {len(nets_to_include)} nets.")
|
|
2299
|
-
else:
|
|
2300
|
-
self.logger.info("Creating cutout on all nets.") # pragma: no cover
|
|
2301
|
-
|
|
2302
|
-
# Check Padstack Instances overlapping the cutout
|
|
2303
|
-
pinstance_to_add = []
|
|
2304
|
-
if include_partial_instances:
|
|
2305
|
-
if nets_to_include:
|
|
2306
|
-
pinst = [i for i in list(self.padstacks.instances.values()) if i.net_name in nets_to_include]
|
|
2307
|
-
else:
|
|
2308
|
-
pinst = [i for i in list(self.padstacks.instances.values())]
|
|
2309
|
-
for p in pinst:
|
|
2310
|
-
pin_position = p.position # check bug #434 status
|
|
2311
|
-
if polygon_data.is_inside(p.position): # check bug #434 status
|
|
2312
|
-
pinstance_to_add.append(p)
|
|
2313
|
-
# validate references in layout
|
|
2314
|
-
for _ref in self.nets.nets:
|
|
2315
|
-
if nets_to_include:
|
|
2316
|
-
if _ref in nets_to_include:
|
|
2317
|
-
_ref_nets.append(self.nets.nets[_ref])
|
|
2318
|
-
else:
|
|
2319
|
-
_ref_nets.append(self.nets.nets[_ref]) # pragma: no cover
|
|
2320
|
-
if keep_voids:
|
|
2321
|
-
voids = [p for p in self.modeler.circles if p.is_void]
|
|
2322
|
-
voids2 = [p for p in self.modeler.polygons if p.is_void]
|
|
2323
|
-
voids.extend(voids2)
|
|
2324
|
-
else:
|
|
2325
|
-
voids = []
|
|
2326
|
-
voids_to_add = []
|
|
2327
|
-
for circle in voids:
|
|
2328
|
-
if polygon_data.get_intersection_type(circle.polygon_data) >= 3:
|
|
2329
|
-
voids_to_add.append(circle)
|
|
2330
|
-
|
|
2331
|
-
_netsClip = _ref_nets
|
|
2332
|
-
# Create new cutout cell/design
|
|
2333
|
-
_cutout = self.active_cell.cutout(_netsClip, _netsClip, polygon_data)
|
|
2334
|
-
layout = _cutout.layout
|
|
2335
|
-
cutout_obj_coll = layout.padstack_instances
|
|
2336
|
-
ids = []
|
|
2337
|
-
for lobj in cutout_obj_coll:
|
|
2338
|
-
ids.append(lobj.id)
|
|
2339
|
-
if include_partial_instances:
|
|
2340
|
-
from ansys.edb.core.geometry.point_data import PointData as GrpcPointData
|
|
2341
|
-
from ansys.edb.core.primitive.padstack_instance import (
|
|
2342
|
-
PadstackInstance as GrpcPadstackInstance,
|
|
2343
|
-
)
|
|
2344
|
-
|
|
2345
|
-
p_missing = [i for i in pinstance_to_add if i.id not in ids]
|
|
2346
|
-
self.logger.info(f"Added {len(p_missing)} padstack instances after cutout")
|
|
2347
|
-
for p in p_missing:
|
|
2348
|
-
position = GrpcPointData(p.position)
|
|
2349
|
-
net = self.nets.find_or_create_net(p.net_name)
|
|
2350
|
-
rotation = Value(p.rotation)
|
|
2351
|
-
sign_layers = list(self.stackup.signal_layers.keys())
|
|
2352
|
-
if not p.start_layer: # pragma: no cover
|
|
2353
|
-
fromlayer = self.stackup.signal_layers[sign_layers[0]]
|
|
2354
|
-
else:
|
|
2355
|
-
fromlayer = self.stackup.signal_layers[p.start_layer]
|
|
2356
|
-
|
|
2357
|
-
if not p.stop_layer: # pragma: no cover
|
|
2358
|
-
tolayer = self.stackup.signal_layers[sign_layers[-1]]
|
|
2359
|
-
else:
|
|
2360
|
-
tolayer = self.stackup.signal_layers[p.stop_layer]
|
|
2361
|
-
for pad in list(self.padstacks.definitions.keys()):
|
|
2362
|
-
if pad == p.padstack_definition:
|
|
2363
|
-
padstack = self.padstacks.definitions[pad]
|
|
2364
|
-
padstack_instance = GrpcPadstackInstance.create(
|
|
2365
|
-
layout=_cutout.layout,
|
|
2366
|
-
net=net,
|
|
2367
|
-
name=p.name,
|
|
2368
|
-
padstack_def=padstack,
|
|
2369
|
-
position_x=position.x,
|
|
2370
|
-
position_y=position.y,
|
|
2371
|
-
rotation=rotation,
|
|
2372
|
-
top_layer=fromlayer,
|
|
2373
|
-
bottom_layer=tolayer,
|
|
2374
|
-
layer_map=None,
|
|
2375
|
-
solder_ball_layer=None,
|
|
2376
|
-
)
|
|
2377
|
-
padstack_instance.is_layout_pin = p.is_pin
|
|
2378
|
-
break
|
|
2379
|
-
|
|
2380
|
-
for void_circle in voids_to_add:
|
|
2381
|
-
if isinstance(void_circle, Circle):
|
|
2382
|
-
res = void_circle.get_parameters()
|
|
2383
|
-
cloned_circle = Circle.create(
|
|
2384
|
-
layout=layout,
|
|
2385
|
-
layer=void_circle.layer.name,
|
|
2386
|
-
net=void_circle.net,
|
|
2387
|
-
center_x=res[0].x,
|
|
2388
|
-
center_y=res[0].y,
|
|
2389
|
-
radius=res[1],
|
|
2390
|
-
)
|
|
2391
|
-
cloned_circle.is_negative = True
|
|
2392
|
-
elif isinstance(void_circle, Polygon):
|
|
2393
|
-
cloned_polygon = Polygon.create(
|
|
2394
|
-
layout,
|
|
2395
|
-
void_circle.layer.name,
|
|
2396
|
-
void_circle.net,
|
|
2397
|
-
void_circle.polygon_data,
|
|
2398
|
-
)
|
|
2399
|
-
cloned_polygon.is_negative = True
|
|
2400
|
-
layers = [i for i in list(self.stackup.signal_layers.keys())]
|
|
2401
|
-
for layer in layers:
|
|
2402
|
-
layer_primitves = self.modeler.get_primitives(layer_name=layer)
|
|
2403
|
-
if len(layer_primitves) == 0:
|
|
2404
|
-
self.modeler.create_polygon(point_list, layer, net_name="DUMMY")
|
|
2405
|
-
self.logger.info(f"Cutout {_cutout.name} created correctly")
|
|
2406
|
-
for _setup in self.active_cell.simulation_setups:
|
|
2407
|
-
# Add the create Simulation setup to cutout cell
|
|
2408
|
-
# might need to add a clone setup method.
|
|
2409
|
-
pass
|
|
2410
|
-
|
|
2411
|
-
_dbCells = [_cutout]
|
|
2412
|
-
if output_aedb_path:
|
|
2413
|
-
from ansys.edb.core.database import Database as GrpcDatabase
|
|
2414
|
-
|
|
2415
|
-
db2 = GrpcDatabase.create(output_aedb_path)
|
|
2416
|
-
db2.save()
|
|
2417
|
-
cell_copied = db2.copy_cells(_dbCells) # Copies cutout cell/design to db2 project
|
|
2418
|
-
cell = cell_copied[0]
|
|
2419
|
-
cell.name = os.path.basename(output_aedb_path[:-5])
|
|
2420
|
-
db2.save()
|
|
2421
|
-
for c in list(self.active_db.top_circuit_cells):
|
|
2422
|
-
if c.name == _cutout.name:
|
|
2423
|
-
c.delete()
|
|
2424
|
-
if open_cutout_at_end: # pragma: no cover
|
|
2425
|
-
db2.save()
|
|
2426
|
-
self._db = db2
|
|
2427
|
-
self.edbpath = output_aedb_path
|
|
2428
|
-
self._active_cell = cell
|
|
2429
|
-
self.edbpath = self.directory
|
|
2430
|
-
self._init_objects()
|
|
2431
|
-
else:
|
|
2432
|
-
db2.close()
|
|
2433
|
-
source = os.path.join(output_aedb_path, "edb.def.tmp")
|
|
2434
|
-
target = os.path.join(output_aedb_path, "edb.def")
|
|
2435
|
-
self._wait_for_file_release(file_to_release=output_aedb_path)
|
|
2436
|
-
if os.path.exists(source) and not os.path.exists(target):
|
|
2437
|
-
try:
|
|
2438
|
-
shutil.copy(source, target)
|
|
2439
|
-
self.logger.warning("aedb def file manually created.")
|
|
2440
|
-
except Exception as e:
|
|
2441
|
-
self.logger.error(f"Failed to copy {source} to {target} - {type(e).__name__}: {str(e)}")
|
|
2442
|
-
return [[Value(pt.x), Value(pt.y)] for pt in polygon_data.without_arcs().points]
|
|
1640
|
+
>>> from pyedb import Edb
|
|
1641
|
+
>>> edb = Edb(r"C:\\test.aedb", version="2022.2")
|
|
1642
|
+
>>> edb.logger.info_timer("Edb Opening")
|
|
1643
|
+
>>> edb.logger.reset_timer()
|
|
1644
|
+
>>> start = time.time()
|
|
1645
|
+
>>> signal_list = []
|
|
1646
|
+
>>> for net in edb.nets.netlist:
|
|
1647
|
+
>>> if "3V3" in net:
|
|
1648
|
+
>>> signal_list.append(net)
|
|
1649
|
+
>>> power_list = ["PGND"]
|
|
1650
|
+
>>> edb.cutout(signal_nets=signal_list, reference_nets=power_list, extent_type="Conforming")
|
|
1651
|
+
>>> end_time = str((time.time() - start) / 60)
|
|
1652
|
+
>>> edb.logger.info("Total legacy cutout time in min %s", end_time)
|
|
1653
|
+
>>> edb.nets.plot(signal_list, None, color_by_net=True)
|
|
1654
|
+
>>> edb.nets.plot(power_list, None, color_by_net=True)
|
|
1655
|
+
>>> edb.save()
|
|
1656
|
+
>>> edb.close()
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
"""
|
|
1660
|
+
cutout = Cutout(self)
|
|
1661
|
+
cutout.expansion_size = expansion_size
|
|
1662
|
+
cutout.signals = signal_nets
|
|
1663
|
+
cutout.references = reference_nets
|
|
1664
|
+
cutout.extent_type = extent_type
|
|
1665
|
+
cutout.expansion_size = expansion_size
|
|
1666
|
+
cutout.use_round_corner = use_round_corner
|
|
1667
|
+
cutout.output_file = output_aedb_path
|
|
1668
|
+
cutout.open_cutout_at_end = open_cutout_at_end
|
|
1669
|
+
cutout.use_pyaedt_cutout = use_pyaedt_cutout
|
|
1670
|
+
cutout.number_of_threads = number_of_threads
|
|
1671
|
+
cutout.use_pyaedt_extent_computing = use_pyaedt_extent_computing
|
|
1672
|
+
cutout.extent_defeatured = extent_defeature
|
|
1673
|
+
cutout.remove_single_pin_components = remove_single_pin_components
|
|
1674
|
+
cutout.custom_extent = custom_extent
|
|
1675
|
+
cutout.custom_extent_units = custom_extent_units
|
|
1676
|
+
cutout.include_partial_instances = include_partial_instances
|
|
1677
|
+
cutout.keep_voids = keep_voids
|
|
1678
|
+
cutout.check_terminals = check_terminals
|
|
1679
|
+
cutout.include_pingroups = include_pingroups
|
|
1680
|
+
cutout.expansion_factor = expansion_factor
|
|
1681
|
+
cutout.maximum_iterations = maximum_iterations
|
|
1682
|
+
cutout.preserve_components_with_model = preserve_components_with_model
|
|
1683
|
+
cutout.simple_pad_check = simple_pad_check
|
|
1684
|
+
cutout.keep_lines_as_path = keep_lines_as_path
|
|
1685
|
+
cutout.include_voids_in_extents = include_voids_in_extents
|
|
1686
|
+
return cutout.run()
|
|
2443
1687
|
|
|
2444
1688
|
@staticmethod
|
|
2445
1689
|
def write_export3d_option_config_file(path_to_output, config_dictionaries=None):
|
|
@@ -2880,55 +2124,51 @@ class Edb(EdbInit):
|
|
|
2880
2124
|
self.logger.reset_timer()
|
|
2881
2125
|
if not common_reference:
|
|
2882
2126
|
ref_terminals = [term for term in all_sources if term.is_reference_terminal]
|
|
2883
|
-
common_reference = list(
|
|
2884
|
-
set([i.reference_terminal.net.name for i in all_sources if i.is_reference_terminal])
|
|
2885
|
-
)
|
|
2127
|
+
common_reference = list(set([i.net.name for i in ref_terminals]))
|
|
2886
2128
|
if len(common_reference) > 1:
|
|
2887
|
-
|
|
2888
|
-
return False
|
|
2129
|
+
raise ValueError("Multiple reference nets found. Please specify one.")
|
|
2889
2130
|
if not common_reference:
|
|
2890
|
-
|
|
2891
|
-
return False
|
|
2892
|
-
|
|
2131
|
+
raise ValueError("No reference net found. Please specify one.")
|
|
2893
2132
|
common_reference = common_reference[0]
|
|
2894
2133
|
all_sources = [i for i in all_sources if i.net.name != common_reference]
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
]
|
|
2901
|
-
if len(
|
|
2134
|
+
layout_inst = self.layout.layout_instance
|
|
2135
|
+
layout_obj_inst = layout_inst.get_layout_obj_instance_in_context(all_sources[0], None) # 2nd arg was []
|
|
2136
|
+
connected_objects = [loi.layout_obj.id for loi in layout_inst.get_connected_objects(layout_obj_inst, True)]
|
|
2137
|
+
connected_primitives = [self.modeler.get_primitive(obj, edb_uid=False) for obj in connected_objects]
|
|
2138
|
+
connected_primitives = [item for item in connected_primitives if item is not None]
|
|
2139
|
+
set_list = list(set([obj.net_name for obj in connected_primitives]))
|
|
2140
|
+
if len(set_list) != len(all_sources):
|
|
2902
2141
|
self.logger.error("No Reference found.")
|
|
2903
2142
|
return False
|
|
2904
2143
|
cmps = [
|
|
2905
2144
|
i
|
|
2906
2145
|
for i in list(self.components.resistors.values())
|
|
2907
|
-
if i.
|
|
2146
|
+
if i.num_pins == 2 and common_reference in i.nets and i.res_value <= 1
|
|
2908
2147
|
]
|
|
2909
2148
|
cmps.extend(
|
|
2910
|
-
[i for i in list(self.components.inductors.values()) if i.
|
|
2149
|
+
[i for i in list(self.components.inductors.values()) if i.num_pins == 2 and common_reference in i.nets]
|
|
2911
2150
|
)
|
|
2912
2151
|
|
|
2913
2152
|
for cmp in cmps:
|
|
2914
2153
|
found = False
|
|
2915
|
-
ids = [
|
|
2916
|
-
for list_obj in
|
|
2154
|
+
ids = [v.id for i, v in cmp.pins.items()]
|
|
2155
|
+
for list_obj in set_list:
|
|
2917
2156
|
if len(set(ids).intersection(list_obj)) == 1:
|
|
2918
|
-
for list_obj2 in
|
|
2157
|
+
for list_obj2 in set_list:
|
|
2919
2158
|
if list_obj2 != list_obj and len(set(ids).intersection(list_obj)) == 1:
|
|
2920
2159
|
if (ids[0] in list_obj and ids[1] in list_obj2) or (
|
|
2921
2160
|
ids[1] in list_obj and ids[0] in list_obj2
|
|
2922
2161
|
):
|
|
2923
|
-
|
|
2924
|
-
|
|
2162
|
+
set_list[set_list.index(list_obj)] = list_obj.union(list_obj2)
|
|
2163
|
+
set_list[set_list.index(list_obj2)] = list_obj.union(list_obj2)
|
|
2925
2164
|
found = True
|
|
2926
2165
|
break
|
|
2927
2166
|
if found:
|
|
2928
2167
|
break
|
|
2929
2168
|
|
|
2930
2169
|
# Get the set intersections for all the ID sets.
|
|
2931
|
-
|
|
2170
|
+
set_list = set(set_list)
|
|
2171
|
+
iDintersection = set.intersection(set_list)
|
|
2932
2172
|
self.logger.info_timer(f"Terminal reference primitive IDs total intersections = {len(iDintersection)}\n\n")
|
|
2933
2173
|
|
|
2934
2174
|
# If the intersections are non-zero, the terminal references are connected.
|
|
@@ -3956,7 +3196,7 @@ class Edb(EdbInit):
|
|
|
3956
3196
|
except subprocess.CalledProcessError as e: # nosec
|
|
3957
3197
|
raise RuntimeError(
|
|
3958
3198
|
"EDBDiff.exe execution failed. Please check if the executable is present in the base path."
|
|
3959
|
-
)
|
|
3199
|
+
) from e
|
|
3960
3200
|
|
|
3961
3201
|
if not os.path.exists(os.path.join(results, "EDBDiff.csv")):
|
|
3962
3202
|
self.logger.error("Comparison execution failed")
|