From 00250e4cc788908811b910458f9ed28f1e914ecb Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Tue, 25 Mar 2025 20:54:21 +0100 Subject: [PATCH 01/27] grpc transition PR#1005 tracker --- src/pyedb/grpc/database/layout/layout.py | 49 +++++++++++++++++-- .../database/primitive/padstack_instance.py | 2 +- tests/grpc/system/test_edb_layout.py | 9 +++- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/pyedb/grpc/database/layout/layout.py b/src/pyedb/grpc/database/layout/layout.py index 4effe3e979..c223b36b23 100644 --- a/src/pyedb/grpc/database/layout/layout.py +++ b/src/pyedb/grpc/database/layout/layout.py @@ -26,6 +26,7 @@ from typing import Union from ansys.edb.core.layout.layout import Layout as GrpcLayout +import ansys.edb.core.primitive.primitive from pyedb.grpc.database.hierarchy.component import Component from pyedb.grpc.database.hierarchy.pingroup import PinGroup @@ -34,7 +35,12 @@ from pyedb.grpc.database.net.extended_net import ExtendedNet from pyedb.grpc.database.net.net import Net from pyedb.grpc.database.net.net_class import NetClass +from pyedb.grpc.database.primitive.bondwire import Bondwire +from pyedb.grpc.database.primitive.circle import Circle from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance +from pyedb.grpc.database.primitive.path import Path +from pyedb.grpc.database.primitive.polygon import Polygon +from pyedb.grpc.database.primitive.rectangle import Rectangle from pyedb.grpc.database.terminal.bundle_terminal import BundleTerminal from pyedb.grpc.database.terminal.edge_terminal import EdgeTerminal from pyedb.grpc.database.terminal.padstack_instance_terminal import ( @@ -59,6 +65,26 @@ def cell(self): """ return self._pedb._active_cell + @property + def primitives(self): + prims = [] + for prim in super().primitives: + if isinstance(prim, ansys.edb.core.primitive.primitive.Path): + prims.append(Path(self._pedb, prim)) + elif isinstance(prim, ansys.edb.core.primitive.primitive.Polygon): + prims.append(Polygon(self._pedb, prim)) + elif isinstance(prim, ansys.edb.core.primitive.primitive.PadstackInstance): + prims.append(PadstackInstance(self._pedb, prim)) + elif isinstance(prim, ansys.edb.core.primitive.primitive.Rectangle): + prims.append(Rectangle(self._pedb, prim)) + elif isinstance(prim, ansys.edb.core.primitive.primitive.Circle): + prims.append(Circle(self._pedb, prim)) + elif isinstance(prim, ansys.edb.core.primitive.primitive.Bondwire): + prims.append(Bondwire(self._pedb, prim)) + else: + raise "Not valid primitive." + return prims + @property def terminals(self): """Get terminals belonging to active layout. @@ -180,17 +206,32 @@ def voltage_regulators(self): """ return [VoltageRegulator(self._pedb, i) for i in self._pedb.active_cell.layout.voltage_regulators] - def find_primitive(self, layer_name: Union[str, list]) -> list: + def find_primitive( + self, layer_name: Union[str, list] = None, name: Union[str, list] = None, net_name: Union[str, list] = None + ) -> list: """Find a primitive objects by layer name. - Parameters ---------- layer_name : str, list + layer_name : str, list, optional Name of the layer. + name : str, list, optional + Name of the primitive + net_name : str, list, optional + Name of the primitive Returns ------- List[:class:`Primitive Date: Fri, 11 Apr 2025 14:45:50 +0200 Subject: [PATCH 02/27] grpc test_01a_setups_frequency_sweeps --- src/pyedb/configuration/cfg_setup.py | 5 ++++- tests/grpc/system/test_edb_configuration_2p0.py | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pyedb/configuration/cfg_setup.py b/src/pyedb/configuration/cfg_setup.py index 5a5e6955ce..1f9ab46c26 100644 --- a/src/pyedb/configuration/cfg_setup.py +++ b/src/pyedb/configuration/cfg_setup.py @@ -60,7 +60,10 @@ def _apply_freq_sweep(self, edb_setup): f_set.append([f.distribution, f.start, f.stop, f.increment]) else: kw[attr] = getattr(i, attr) - edb_setup.add_sweep(i.name, frequency_set=f_set, **kw) + if self._pedb.grpc: + edb_setup.add_sweep(name=i.name) + else: + edb_setup.add_sweep(i.name, frequency_set=f_set, **kw) class CfgSIwaveACSetup(CfgSetup): diff --git a/tests/grpc/system/test_edb_configuration_2p0.py b/tests/grpc/system/test_edb_configuration_2p0.py index 468cb575d7..b148d058a3 100644 --- a/tests/grpc/system/test_edb_configuration_2p0.py +++ b/tests/grpc/system/test_edb_configuration_2p0.py @@ -179,7 +179,13 @@ def test_01a_setups_frequency_sweeps(self, edb_examples): if sw_p_name == "frequencies": pass else: - assert sw_value == target_sw[sw_p_name] + if edbapp.grpc: + if sw_value == "interpolation": + assert target_sw[sw_p_name].name == "INTERPOLATING_SWEEP" + else: + assert sw_value == target_sw[sw_p_name] + else: + assert sw_value == target_sw[sw_p_name] else: assert value == target[p] edbapp.close() From 28d668090fa2f7dbeb0f2aef32ead10cdf94a21a Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 5 May 2025 15:26:43 +0200 Subject: [PATCH 03/27] grpc test fix --- src/pyedb/configuration/cfg_boundaries.py | 2 +- .../configuration/cfg_s_parameter_models.py | 52 ++++++++++++++++++- src/pyedb/grpc/database/components.py | 3 +- .../grpc/database/definition/component_def.py | 2 +- src/pyedb/grpc/edb.py | 1 - tests/grpc/system/conftest.py | 8 +-- .../grpc/system/test_edb_configuration_2p0.py | 1 + 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/pyedb/configuration/cfg_boundaries.py b/src/pyedb/configuration/cfg_boundaries.py index a5c1696f14..4f773c2426 100644 --- a/src/pyedb/configuration/cfg_boundaries.py +++ b/src/pyedb/configuration/cfg_boundaries.py @@ -79,7 +79,7 @@ def set_parameters_to_edb(self): if self.air_box_horizontal_padding: self._pedb.hfss.hfss_extent_info.air_box_horizontal_extent = float(self.air_box_horizontal_padding) if self.air_box_positive_vertical_padding: - self._pedb.hfss.hfss_extent_info.parentair_box_positive_vertical_extent = float( + self._pedb.hfss.hfss_extent_info.air_box_positive_vertical_extent = float( self.air_box_positive_vertical_padding ) if self.air_box_negative_vertical_padding: diff --git a/src/pyedb/configuration/cfg_s_parameter_models.py b/src/pyedb/configuration/cfg_s_parameter_models.py index 10cf562cab..89f6968f05 100644 --- a/src/pyedb/configuration/cfg_s_parameter_models.py +++ b/src/pyedb/configuration/cfg_s_parameter_models.py @@ -80,7 +80,7 @@ def get_data_from_db(self, cfg_components): else: pin_order = compdef_obj.get_properties()["pin_order"] temp_comps = [i for i in cfg_components if i["definition"] == name] - for model_name, model_obj in nport_models.items(): + for model_obj in nport_models: temp_comp_list = [] reference_net_per_component = {} for i in temp_comps: @@ -155,6 +155,56 @@ def apply(self): ref_net = s_param.reference_net comp.use_s_parameter_model(s_param.name, reference_net=ref_net) + def get_data_from_db(self, cfg_components): + db_comp_def = self._pedb.definitions.component + for name, compdef_obj in db_comp_def.items(): + nport_models = compdef_obj.component_models + if not nport_models: + continue + else: + pin_order = compdef_obj.get_properties()["pin_order"] + temp_comps = [i for i in cfg_components if i["definition"] == name] + for model_name, model_obj in nport_models.items(): + temp_comp_list = [] + reference_net_per_component = {} + for i in temp_comps: + s_param_model = i.get("s_parameter_model") + if s_param_model: + if s_param_model["model_name"] == model_name: + temp_comp_list.append(i["reference_designator"]) + reference_net_per_component[i["reference_designator"]] = s_param_model[ + "reference_net" + ] + else: + continue + + self.parent.s_parameters_models.append( + CfgSParameterModel( + name=model_name, + component_definition=name, + file_path=model_obj.reference_file, + apply_to_all=False, + components=temp_comp_list, + reference_net_per_component=reference_net_per_component, + pin_order=pin_order, + ) + ) + + data = [] + for i in self.parent.s_parameters_models: + data.append( + { + "name": i.name, + "component_definition": i.component_definition, + "file_path": i.file_path, + "apply_to_all": i.apply_to_all, + "components": i.components, + "reference_net_per_component": i.reference_net_per_component, + "pin_order": i.pin_order, + } + ) + return data + def __init__(self, pedb, data, path_lib=None): self._pedb = pedb if self._pedb.grpc: diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index f6fae7298e..ccb547c920 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -178,8 +178,7 @@ def definitions(self): @property def nport_comp_definition(self): """Retrieve Nport component definition list.""" - m = "Ansys.Ansoft.Edb.Definition.NPortComponentModel" - return {name: l for name, l in self.definitions.items() if m in [i for i in l.model]} + return {name: l for name, l in self.definitions.items() if l.reference_file} def import_definition(self, file_path): """Import component definition from json file. diff --git a/src/pyedb/grpc/database/definition/component_def.py b/src/pyedb/grpc/database/definition/component_def.py index 1746304886..b24d79d4b3 100644 --- a/src/pyedb/grpc/database/definition/component_def.py +++ b/src/pyedb/grpc/database/definition/component_def.py @@ -230,4 +230,4 @@ def set_properties(self, **kwargs): if pin_order: old = {i.name: i for i in self.component_pins} temp = [old[str(i)] for i in pin_order] - self.component_pins = temp + self.reorder_pins(temp) diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index afb4a2dbb7..1df972de28 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -570,7 +570,6 @@ def open_edb(self, restart_rpc_server=False, kill_all_instances=False): self.edbpath, self.isreadonly, restart_rpc_server=restart_rpc_server, - kill_all_instances=kill_all_instances, ) n_try -= 1 except Exception as e: diff --git a/tests/grpc/system/conftest.py b/tests/grpc/system/conftest.py index 33df5e88f6..fa75443c90 100644 --- a/tests/grpc/system/conftest.py +++ b/tests/grpc/system/conftest.py @@ -84,22 +84,22 @@ def get_si_verse(self, edbapp=True, additional_files_folders="", version=None): self.local_scratch.copyfolder(src, file_folder_name) if edbapp: version = desktop_version if version is None else version - return Edb(aedb, edbversion=version, restart_rpc_server=True, kill_all_instances=True) + return Edb(aedb, edbversion=version, restart_rpc_server=True) else: return aedb def create_empty_edb(self): local_folder = self._create_test_folder() aedb = os.path.join(local_folder, "new_layout.aedb") - return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True, kill_all_instances=True) + return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True) def get_multizone_pcb(self): aedb = self._copy_file_folder_into_local_folder("multi_zone_project.aedb") - return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True, kill_all_instances=True) + return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True) def get_no_ref_pins_component(self): aedb = self._copy_file_folder_into_local_folder("TEDB/component_no_ref_pins.aedb") - return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True, kill_all_instances=True) + return Edb(aedb, edbversion=desktop_version, restart_rpc_server=True) @pytest.fixture(scope="class") diff --git a/tests/grpc/system/test_edb_configuration_2p0.py b/tests/grpc/system/test_edb_configuration_2p0.py index a10e57a1d5..697fd6f9e1 100644 --- a/tests/grpc/system/test_edb_configuration_2p0.py +++ b/tests/grpc/system/test_edb_configuration_2p0.py @@ -494,6 +494,7 @@ def test_05h_diff_wave_port(self, edb_examples): edbapp.close() def test_06_s_parameters(self, edb_examples): + # TODO check bug #542 status. Seems some API's are missing. data = { "general": {"s_parameter_library": self.local_input_folder}, "s_parameters": [ From 4bf6e8bf00815a1b61e973f1a32a1d7440ad18fb Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Tue, 27 May 2025 14:14:26 +0200 Subject: [PATCH 04/27] temp --- .../configuration/cfg_boundaries_data.py | 32 ++++++ .../configuration/cfg_components_data.py | 84 ++++++++++++++++ src/pyedb/configuration/cfg_general_data.py | 19 ++++ src/pyedb/configuration/cfg_nets_data.py | 19 ++++ .../configuration/cfg_operations_data.py | 40 ++++++++ .../cfg_package_definition_data.py | 46 +++++++++ src/pyedb/configuration/cfg_padsatck_data.py | 97 ++++++++++++++++++ src/pyedb/configuration/cfg_pingroup_data.py | 99 +++++++++++++++++++ .../configuration/cfg_ports_sources_data.py | 47 +++++++++ 9 files changed, 483 insertions(+) create mode 100644 src/pyedb/configuration/cfg_boundaries_data.py create mode 100644 src/pyedb/configuration/cfg_components_data.py create mode 100644 src/pyedb/configuration/cfg_general_data.py create mode 100644 src/pyedb/configuration/cfg_nets_data.py create mode 100644 src/pyedb/configuration/cfg_operations_data.py create mode 100644 src/pyedb/configuration/cfg_package_definition_data.py create mode 100644 src/pyedb/configuration/cfg_padsatck_data.py create mode 100644 src/pyedb/configuration/cfg_pingroup_data.py create mode 100644 src/pyedb/configuration/cfg_ports_sources_data.py diff --git a/src/pyedb/configuration/cfg_boundaries_data.py b/src/pyedb/configuration/cfg_boundaries_data.py new file mode 100644 index 0000000000..45287dbe99 --- /dev/null +++ b/src/pyedb/configuration/cfg_boundaries_data.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass + + +@dataclass +class CfgBoundaries: + open_region: bool = True + open_region_type: str = "radiation" + pml_visible: bool = False + pml_operation_frequency: str = "5ghz" + pml_radiation_factor: int = 10 + dielectric_extents_type: str = "bounding_box" + dielectric_base_polygon: str = "" + horizontal_padding: float = 0 + honor_primitives_on_dielectric_layers: bool = type + air_box_extents_type: str = "bounding_box" + air_box_base_polygon: str = "" + air_box_truncate_model_ground_layers: bool = False + air_box_horizontal_padding: float = 0.15 + air_box_positive_vertical_padding: float = 1 + air_box_negative_vertical_padding: float = 1 + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_components_data.py b/src/pyedb/configuration/cfg_components_data.py new file mode 100644 index 0000000000..0538fe04a6 --- /dev/null +++ b/src/pyedb/configuration/cfg_components_data.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass + + +@dataclass +class CfgPinPair: + type: str + p1: str = "1" + p2: str = "2" + capacitance = "" + inductance = "" + resistance = "" + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgSolderBallProperties: + shape: str = "cylinder" + diameter: str = "150um" + height: str = "100um" + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgPortProperties: + reference_offset: float = 0 + reference_size_auto: bool = True + reference_size_x: float = 0.0 + reference_size_y: float = 0.0 + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgComponents: + reference_designator: str + part_type: str + enabled: bool + rlc_model: dict[str, list[CfgPinPair]] + solder_ball_properties: CfgSolderBallProperties + port_properties: CfgPortProperties + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_general_data.py b/src/pyedb/configuration/cfg_general_data.py new file mode 100644 index 0000000000..092697b781 --- /dev/null +++ b/src/pyedb/configuration/cfg_general_data.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass + + +@dataclass +class CfgGeneral: + spice_model_library: str = "" + s_parameter_library: str = "" + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_nets_data.py b/src/pyedb/configuration/cfg_nets_data.py new file mode 100644 index 0000000000..e6420f9a7d --- /dev/null +++ b/src/pyedb/configuration/cfg_nets_data.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass + + +@dataclass +class CfgNets: + power_ground_nets: list[str] + signal_nets: list[str] + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_operations_data.py b/src/pyedb/configuration/cfg_operations_data.py new file mode 100644 index 0000000000..10eae132aa --- /dev/null +++ b/src/pyedb/configuration/cfg_operations_data.py @@ -0,0 +1,40 @@ +from dataclasses import dataclass + + +@dataclass +class CfgCutout: + signal_list: [str] + reference_list: [str] + extent_type: str = "ConvexHull" + expansion_size: float = 0.002 + use_round_corner: bool = False + output_aedb_path: str = "" + open_cutout_at_end: bool = True + use_pyaedt_cutout: bool = True + number_of_threads: int = 4 + use_pyaedt_extent_computing: bool = True + extent_defeature: float = 0.0 + remove_single_pin_components: bool = False + custom_extent: str = "" + custom_extent_units: str = "mm" + include_partial_instances: bool = False + keep_voids: bool = True + check_terminals: bool = False + include_pingroups: bool = False + expansion_factor: float = 0.0 + maximum_iterations: int = 30 + preserve_components_with_model: bool = False + simple_pad_check: bool = True + keep_lines_as_path: bool = False + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_package_definition_data.py b/src/pyedb/configuration/cfg_package_definition_data.py new file mode 100644 index 0000000000..c3360a7d1f --- /dev/null +++ b/src/pyedb/configuration/cfg_package_definition_data.py @@ -0,0 +1,46 @@ +from dataclasses import dataclass + + +@dataclass +class CfgHeatSink: + fin_base_height: float + fin_height: float + fin_orientation: str + fin_spacing: float + fin_thickness: float + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgPackageDefinition: + name: str + component_definition: str + maximum_power: float + therm_cond: float + theta_jb: float + theta_jc: float + height: float + heatsink: CfgHeatSink + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_padsatck_data.py b/src/pyedb/configuration/cfg_padsatck_data.py new file mode 100644 index 0000000000..efa7deef65 --- /dev/null +++ b/src/pyedb/configuration/cfg_padsatck_data.py @@ -0,0 +1,97 @@ +from dataclasses import dataclass + + +@dataclass +class CfgDefinition: + name: str + hole_diameter: str + hole_plating_thickness: str + hole_material: str + hole_range: str = "through" + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgBackDrillTop: + drill_to_layer: str + drill_diameter: str + stub_length: str + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgBackDrillBottom: + drill_to_layer: str + drill_diameter: str + stub_length: str + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgInstance: + name: str + backdrill_top: CfgBackDrillTop + backdrill_bottom: CfgBackDrillBottom + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgPadStack: + definitions: list[CfgDefinition] + instances: list[CfgInstance] + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_pingroup_data.py b/src/pyedb/configuration/cfg_pingroup_data.py new file mode 100644 index 0000000000..87149ec9a9 --- /dev/null +++ b/src/pyedb/configuration/cfg_pingroup_data.py @@ -0,0 +1,99 @@ +from dataclasses import dataclass +from typing import Union + + +@dataclass +class CfgPinGroup: + name: str + reference_designator: str + pins: list[str] + net: Union[str, list[str]] + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgTerminal: + pingroup: str + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgPorts: + name: str + type: str + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgSource: + name: str + type: str + magnitude: float + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass + + +@dataclass +class CfgPinGroups: + pin_groups: list[CfgPinGroup] + ports: list[CfgPorts] + sources: list[CfgSource] + + def _import_from_json(self): + pass + + def _export_json(self): + pass + + def _get_from_edb(self) -> bool: + pass + + def _set_to_edb(self) -> bool: + pass diff --git a/src/pyedb/configuration/cfg_ports_sources_data.py b/src/pyedb/configuration/cfg_ports_sources_data.py new file mode 100644 index 0000000000..ff1e8dddc8 --- /dev/null +++ b/src/pyedb/configuration/cfg_ports_sources_data.py @@ -0,0 +1,47 @@ +from dataclasses import dataclass + + +@dataclass +class CfgTerminal: + pin: str + net: str + + +class CfgPort: + name: str + reference_designator: str + type: str + positive_terminal: CfgTerminal + + { + "ports": [ + { + "name": "CIRCUIT_C375_1_2", + "reference_designator": "C375", + "type": "circuit", + "positive_terminal": {"pin": "1"}, + "negative_terminal": {"pin": "2"}, + }, + { + "name": "CIRCUIT_X1_B8_GND", + "reference_designator": "X1", + "type": "circuit", + "positive_terminal": {"pin": "B8"}, + "negative_terminal": {"net": "GND"}, + }, + { + "name": "CIRCUIT_X1_B9_GND", + "reference_designator": "X1", + "type": "circuit", + "positive_terminal": {"net": "PCIe_Gen4_TX2_N"}, + "negative_terminal": {"net": "GND"}, + }, + { + "name": "CIRCUIT_U7_VDD_DDR_GND", + "reference_designator": "U7", + "type": "circuit", + "positive_terminal": {"net": "VDD_DDR"}, + "negative_terminal": {"net": "GND"}, + }, + ] + } From 4c57e1aa6ea0d355d459b5f8937e5d7fde1731bc Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Wed, 28 May 2025 15:32:17 +0200 Subject: [PATCH 05/27] cfg data_model --- .../configuration/cfg_ports_sources_data.py | 47 ------------------- .../{ => data_model}/cfg_boundaries_data.py | 0 .../data_model/cfg_configuration_data.py | 38 +++++++++++++++ .../{ => data_model}/cfg_general_data.py | 0 .../{ => data_model}/cfg_operations_data.py | 13 ++--- .../cfg_package_definition_data.py | 25 ++-------- .../data_model/cfg_ports_sources_data.py | 36 ++++++++++++++ .../data_model/cfg_s_parameter_models_data.py | 18 +++++++ .../data_model/cfg_setup_data.py | 39 +++++++++++++++ .../data_model/cfg_spice_models_data.py | 22 +++++++++ .../data_model/cfg_stackup_data.py | 25 ++++++++++ 11 files changed, 184 insertions(+), 79 deletions(-) delete mode 100644 src/pyedb/configuration/cfg_ports_sources_data.py rename src/pyedb/configuration/{ => data_model}/cfg_boundaries_data.py (100%) create mode 100644 src/pyedb/configuration/data_model/cfg_configuration_data.py rename src/pyedb/configuration/{ => data_model}/cfg_general_data.py (100%) rename src/pyedb/configuration/{ => data_model}/cfg_operations_data.py (82%) rename src/pyedb/configuration/{ => data_model}/cfg_package_definition_data.py (51%) create mode 100644 src/pyedb/configuration/data_model/cfg_ports_sources_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_setup_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_spice_models_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_stackup_data.py diff --git a/src/pyedb/configuration/cfg_ports_sources_data.py b/src/pyedb/configuration/cfg_ports_sources_data.py deleted file mode 100644 index ff1e8dddc8..0000000000 --- a/src/pyedb/configuration/cfg_ports_sources_data.py +++ /dev/null @@ -1,47 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class CfgTerminal: - pin: str - net: str - - -class CfgPort: - name: str - reference_designator: str - type: str - positive_terminal: CfgTerminal - - { - "ports": [ - { - "name": "CIRCUIT_C375_1_2", - "reference_designator": "C375", - "type": "circuit", - "positive_terminal": {"pin": "1"}, - "negative_terminal": {"pin": "2"}, - }, - { - "name": "CIRCUIT_X1_B8_GND", - "reference_designator": "X1", - "type": "circuit", - "positive_terminal": {"pin": "B8"}, - "negative_terminal": {"net": "GND"}, - }, - { - "name": "CIRCUIT_X1_B9_GND", - "reference_designator": "X1", - "type": "circuit", - "positive_terminal": {"net": "PCIe_Gen4_TX2_N"}, - "negative_terminal": {"net": "GND"}, - }, - { - "name": "CIRCUIT_U7_VDD_DDR_GND", - "reference_designator": "U7", - "type": "circuit", - "positive_terminal": {"net": "VDD_DDR"}, - "negative_terminal": {"net": "GND"}, - }, - ] - } diff --git a/src/pyedb/configuration/cfg_boundaries_data.py b/src/pyedb/configuration/data_model/cfg_boundaries_data.py similarity index 100% rename from src/pyedb/configuration/cfg_boundaries_data.py rename to src/pyedb/configuration/data_model/cfg_boundaries_data.py diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py new file mode 100644 index 0000000000..a40d370558 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass + +from pyedb.configuration.data_model.cfg_boundaries_data import CfgBoundaries +from pyedb.configuration.data_model.cfg_components_data import CfgComponents +from pyedb.configuration.data_model.cfg_general_data import CfgGeneral +from pyedb.configuration.data_model.cfg_nets_data import CfgNets +from pyedb.configuration.data_model.cfg_operations_data import CfgOperations +from pyedb.configuration.data_model.cfg_package_definition_data import ( + CfgPackageDefinitions, +) +from pyedb.configuration.data_model.cfg_padsatck_data import CfgPadStacks +from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroups +from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPorts, CfgSources +from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameters +from pyedb.configuration.data_model.cfg_setup_data import CfgSetups +from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModels +from pyedb.configuration.data_model.cfg_stackup_data import CfgStackup + + +@dataclass +class Configuration: + general = CfgGeneral() + boundaries = CfgBoundaries() + nets = CfgNets() + components = CfgComponents() + pin_groups = CfgPinGroups() + sources: CfgSources + ports: CfgPorts + setups: CfgSetups + stackup: CfgStackup + padstacks: CfgPadStacks + s_parameters: CfgSparameters + spice_models: CfgSpiceModels + package_definitions: CfgPackageDefinitions + operations: CfgOperations + # TODO check for variables + # TODO modeler + # TODO probes diff --git a/src/pyedb/configuration/cfg_general_data.py b/src/pyedb/configuration/data_model/cfg_general_data.py similarity index 100% rename from src/pyedb/configuration/cfg_general_data.py rename to src/pyedb/configuration/data_model/cfg_general_data.py diff --git a/src/pyedb/configuration/cfg_operations_data.py b/src/pyedb/configuration/data_model/cfg_operations_data.py similarity index 82% rename from src/pyedb/configuration/cfg_operations_data.py rename to src/pyedb/configuration/data_model/cfg_operations_data.py index 10eae132aa..6b67c0744e 100644 --- a/src/pyedb/configuration/cfg_operations_data.py +++ b/src/pyedb/configuration/data_model/cfg_operations_data.py @@ -27,14 +27,7 @@ class CfgCutout: simple_pad_check: bool = True keep_lines_as_path: bool = False - def _import_from_json(self): - pass - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass +@dataclass +class CfgOperations: + cutout: CfgCutout = None diff --git a/src/pyedb/configuration/cfg_package_definition_data.py b/src/pyedb/configuration/data_model/cfg_package_definition_data.py similarity index 51% rename from src/pyedb/configuration/cfg_package_definition_data.py rename to src/pyedb/configuration/data_model/cfg_package_definition_data.py index c3360a7d1f..5386234058 100644 --- a/src/pyedb/configuration/cfg_package_definition_data.py +++ b/src/pyedb/configuration/data_model/cfg_package_definition_data.py @@ -9,18 +9,6 @@ class CfgHeatSink: fin_spacing: float fin_thickness: float - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - @dataclass class CfgPackageDefinition: @@ -33,14 +21,7 @@ class CfgPackageDefinition: height: float heatsink: CfgHeatSink - def _import_from_json(self): - pass - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass +@dataclass +class CfgPackageDefinitions: + definitions: list[CfgPackageDefinition] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py new file mode 100644 index 0000000000..b3c7ac18bf --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass + + +@dataclass +class CfgTerminal: + pin: str + net: str + + +@dataclass +class CfgPort: + name: str + reference_designator: str + type: str + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + +@dataclass +class CfgSource: + name: str + reference_designator: str + type: str + magnitude: float + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + +@dataclass +class CfgPorts: + ports: [CfgPort] = "default_factory" + + +@dataclass +class CfgSources: + sources: [CfgSource] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py new file mode 100644 index 0000000000..2e687d6bb2 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py @@ -0,0 +1,18 @@ +from dataclasses import dataclass + + +@dataclass +class CfgSparameter: + name: str + component_definition: str + file_path: str + apply_to_all: bool + components: list[str] + reference_net: str + reference_net_per_component: dict[str, str] + + +@dataclass +class CfgSparameters: + general: dict[str, str] = "default_factory" + s_parameters: list[CfgSparameter] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py new file mode 100644 index 0000000000..b240fcc166 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -0,0 +1,39 @@ +from dataclasses import dataclass + + +@dataclass +class CfgFrequency: + distribution: str + start: float + stop: float + step: float + points: int + samples: int + + +@dataclass +class CfgFrequencySweep: + name: str + type: str + frequencies: list[CfgFrequency] + + +@dataclass +class CfgDcIrSettings: + export_dc_thermal_data: bool + + +@dataclass +class CfgSetup: + name: str + type: str + f_adapt: str + max_num_passes: int + max_mag_delta_s: float + dc_slider_position: int + dc_ir_settings: CfgDcIrSettings + + +@dataclass +class CfgSetups: + setups: list[CfgSetup] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_spice_models_data.py b/src/pyedb/configuration/data_model/cfg_spice_models_data.py new file mode 100644 index 0000000000..6541d936a8 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_spice_models_data.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass + + +@dataclass +class CfgSpiceModelLib: + spice_model_library: str + + +@dataclass +class CfgSpiceModel: + name: str + component_definition: str + file_path: str + sub_circuit_name: str + apply_to_all: bool + components: [str] + + +@dataclass +class CfgSpiceModels: + general: CfgSpiceModelLib = None + spice_models: list[CfgSpiceModel] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_stackup_data.py b/src/pyedb/configuration/data_model/cfg_stackup_data.py new file mode 100644 index 0000000000..6cbc1c7eb8 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_stackup_data.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass +from typing import Union + + +@dataclass +class CfgMaterial: + name: str + conductivity: float + permittivity: float + dielectric_loss_tangent: float + + +@dataclass +class CfgLayer: + fill_material: str + material: str + name: str + thickness: Union[str, float] + type: str + + +@dataclass +class CfgStackup: + materials: [CfgMaterial] = "default_factory" + layers: [] = "default_factory" From 089afdff3f509a90cf6bdd687b959a2d7557dfa5 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Wed, 28 May 2025 15:32:36 +0200 Subject: [PATCH 06/27] cfg data_model --- .../configuration/cfg_components_data.py | 84 ---------------- src/pyedb/configuration/cfg_nets_data.py | 19 ---- src/pyedb/configuration/cfg_padsatck_data.py | 97 ------------------ src/pyedb/configuration/cfg_pingroup_data.py | 99 ------------------- .../data_model/cfg_components_data.py | 41 ++++++++ .../configuration/data_model/cfg_nets_data.py | 7 ++ .../data_model/cfg_padsatck_data.py | 37 +++++++ .../data_model/cfg_pingroup_data.py | 39 ++++++++ 8 files changed, 124 insertions(+), 299 deletions(-) delete mode 100644 src/pyedb/configuration/cfg_components_data.py delete mode 100644 src/pyedb/configuration/cfg_nets_data.py delete mode 100644 src/pyedb/configuration/cfg_padsatck_data.py delete mode 100644 src/pyedb/configuration/cfg_pingroup_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_components_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_nets_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_padsatck_data.py create mode 100644 src/pyedb/configuration/data_model/cfg_pingroup_data.py diff --git a/src/pyedb/configuration/cfg_components_data.py b/src/pyedb/configuration/cfg_components_data.py deleted file mode 100644 index 0538fe04a6..0000000000 --- a/src/pyedb/configuration/cfg_components_data.py +++ /dev/null @@ -1,84 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class CfgPinPair: - type: str - p1: str = "1" - p2: str = "2" - capacitance = "" - inductance = "" - resistance = "" - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgSolderBallProperties: - shape: str = "cylinder" - diameter: str = "150um" - height: str = "100um" - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgPortProperties: - reference_offset: float = 0 - reference_size_auto: bool = True - reference_size_x: float = 0.0 - reference_size_y: float = 0.0 - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgComponents: - reference_designator: str - part_type: str - enabled: bool - rlc_model: dict[str, list[CfgPinPair]] - solder_ball_properties: CfgSolderBallProperties - port_properties: CfgPortProperties - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass diff --git a/src/pyedb/configuration/cfg_nets_data.py b/src/pyedb/configuration/cfg_nets_data.py deleted file mode 100644 index e6420f9a7d..0000000000 --- a/src/pyedb/configuration/cfg_nets_data.py +++ /dev/null @@ -1,19 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class CfgNets: - power_ground_nets: list[str] - signal_nets: list[str] - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass diff --git a/src/pyedb/configuration/cfg_padsatck_data.py b/src/pyedb/configuration/cfg_padsatck_data.py deleted file mode 100644 index efa7deef65..0000000000 --- a/src/pyedb/configuration/cfg_padsatck_data.py +++ /dev/null @@ -1,97 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class CfgDefinition: - name: str - hole_diameter: str - hole_plating_thickness: str - hole_material: str - hole_range: str = "through" - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgBackDrillTop: - drill_to_layer: str - drill_diameter: str - stub_length: str - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgBackDrillBottom: - drill_to_layer: str - drill_diameter: str - stub_length: str - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgInstance: - name: str - backdrill_top: CfgBackDrillTop - backdrill_bottom: CfgBackDrillBottom - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgPadStack: - definitions: list[CfgDefinition] - instances: list[CfgInstance] - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass diff --git a/src/pyedb/configuration/cfg_pingroup_data.py b/src/pyedb/configuration/cfg_pingroup_data.py deleted file mode 100644 index 87149ec9a9..0000000000 --- a/src/pyedb/configuration/cfg_pingroup_data.py +++ /dev/null @@ -1,99 +0,0 @@ -from dataclasses import dataclass -from typing import Union - - -@dataclass -class CfgPinGroup: - name: str - reference_designator: str - pins: list[str] - net: Union[str, list[str]] - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgTerminal: - pingroup: str - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgPorts: - name: str - type: str - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgSource: - name: str - type: str - magnitude: float - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass - - -@dataclass -class CfgPinGroups: - pin_groups: list[CfgPinGroup] - ports: list[CfgPorts] - sources: list[CfgSource] - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass diff --git a/src/pyedb/configuration/data_model/cfg_components_data.py b/src/pyedb/configuration/data_model/cfg_components_data.py new file mode 100644 index 0000000000..00bf870b81 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_components_data.py @@ -0,0 +1,41 @@ +from dataclasses import dataclass + + +@dataclass +class CfgPinPair: + type: str + p1: str = "1" + p2: str = "2" + capacitance = "" + inductance = "" + resistance = "" + + +@dataclass +class CfgSolderBallProperties: + shape: str = "cylinder" + diameter: str = "150um" + height: str = "100um" + + +@dataclass +class CfgPortProperties: + reference_offset: float = 0 + reference_size_auto: bool = True + reference_size_x: float = 0.0 + reference_size_y: float = 0.0 + + +@dataclass +class CfgComponent: + reference_designator: str = "" + part_type: str = "" + enabled: bool = True + rlc_model: dict[str, list[CfgPinPair]] = None + solder_ball_properties: CfgSolderBallProperties = None + port_properties: CfgPortProperties = None + + +@dataclass +class CfgComponents: + components: list[CfgComponent] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_nets_data.py b/src/pyedb/configuration/data_model/cfg_nets_data.py new file mode 100644 index 0000000000..7a37b2800f --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_nets_data.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + + +@dataclass +class CfgNets: + power_ground_nets: list[str] = "default_factory" + signal_nets: list[str] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_padsatck_data.py b/src/pyedb/configuration/data_model/cfg_padsatck_data.py new file mode 100644 index 0000000000..de85f12fa1 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_padsatck_data.py @@ -0,0 +1,37 @@ +from dataclasses import dataclass + + +@dataclass +class CfgDefinition: + name: str + hole_diameter: str + hole_plating_thickness: str + hole_material: str + hole_range: str = "through" + + +@dataclass +class CfgBackDrillTop: + drill_to_layer: str + drill_diameter: str + stub_length: str + + +@dataclass +class CfgBackDrillBottom: + drill_to_layer: str + drill_diameter: str + stub_length: str + + +@dataclass +class CfgInstance: + name: str + backdrill_top: CfgBackDrillTop + backdrill_bottom: CfgBackDrillBottom + + +@dataclass +class CfgPadStacks: + definitions: list[CfgDefinition] = "default_factory" + instances: list[CfgInstance] = "default_factory" diff --git a/src/pyedb/configuration/data_model/cfg_pingroup_data.py b/src/pyedb/configuration/data_model/cfg_pingroup_data.py new file mode 100644 index 0000000000..3c6c4104d1 --- /dev/null +++ b/src/pyedb/configuration/data_model/cfg_pingroup_data.py @@ -0,0 +1,39 @@ +from dataclasses import dataclass +from typing import Union + + +@dataclass +class CfgPinGroup: + name: str + reference_designator: str + pins: list[str] + net: Union[str, list[str]] + + +@dataclass +class CfgTerminal: + pingroup: str + + +@dataclass +class CfgPorts: + name: str + type: str + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + +@dataclass +class CfgSource: + name: str + type: str + magnitude: float + positive_terminal: CfgTerminal + negative_terminal: CfgTerminal + + +@dataclass +class CfgPinGroups: + pin_groups: list[CfgPinGroup] = "default_factory" + ports: list[CfgPorts] = "default_factory" + sources: list[CfgSource] = "default_factory" From a890d77081c13bfb20524fb8f942278b6664b88e Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Fri, 30 May 2025 16:14:57 +0200 Subject: [PATCH 07/27] cfg data model load json --- .../data_model/cfg_boundaries_data.py | 15 +---- .../data_model/cfg_components_data.py | 33 +++++++---- .../data_model/cfg_configuration_data.py | 59 ++++++++++++++----- .../data_model/cfg_general_data.py | 19 ++---- .../configuration/data_model/cfg_nets_data.py | 9 ++- .../data_model/cfg_operations_data.py | 10 +++- .../data_model/cfg_package_definition_data.py | 35 ++++++----- .../data_model/cfg_padsatck_data.py | 39 +++++++----- .../data_model/cfg_pingroup_data.py | 43 ++++++++------ .../data_model/cfg_ports_sources_data.py | 39 +++++++----- .../data_model/cfg_s_parameter_models_data.py | 24 ++++---- .../data_model/cfg_setup_data.py | 45 ++++++++------ .../data_model/cfg_spice_models_data.py | 23 +++++--- .../data_model/cfg_stackup_data.py | 29 +++++---- src/pyedb/grpc/database/components.py | 50 ++++++++++++++++ 15 files changed, 301 insertions(+), 171 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_boundaries_data.py b/src/pyedb/configuration/data_model/cfg_boundaries_data.py index 45287dbe99..5aa97ba39e 100644 --- a/src/pyedb/configuration/data_model/cfg_boundaries_data.py +++ b/src/pyedb/configuration/data_model/cfg_boundaries_data.py @@ -1,6 +1,9 @@ from dataclasses import dataclass +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgBoundaries: open_region: bool = True @@ -18,15 +21,3 @@ class CfgBoundaries: air_box_horizontal_padding: float = 0.15 air_box_positive_vertical_padding: float = 1 air_box_negative_vertical_padding: float = 1 - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass diff --git a/src/pyedb/configuration/data_model/cfg_components_data.py b/src/pyedb/configuration/data_model/cfg_components_data.py index 00bf870b81..8051e9fb33 100644 --- a/src/pyedb/configuration/data_model/cfg_components_data.py +++ b/src/pyedb/configuration/data_model/cfg_components_data.py @@ -1,23 +1,34 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgPinPair: - type: str + type: str = "series" p1: str = "1" p2: str = "2" - capacitance = "" - inductance = "" - resistance = "" + capacitance: str = None + inductance: str = None + resistance: str = None + + +@dataclass_json +@dataclass +class CfgPinPairs: + pin_pairs: list[CfgPinPair] = field(default_factory=list) +@dataclass_json @dataclass class CfgSolderBallProperties: - shape: str = "cylinder" - diameter: str = "150um" - height: str = "100um" + shape: str = "None" + diameter: str = "0um" + height: str = "0um" +@dataclass_json @dataclass class CfgPortProperties: reference_offset: float = 0 @@ -26,16 +37,18 @@ class CfgPortProperties: reference_size_y: float = 0.0 +@dataclass_json @dataclass class CfgComponent: reference_designator: str = "" part_type: str = "" enabled: bool = True - rlc_model: dict[str, list[CfgPinPair]] = None + rlc_model: CfgPinPairs = None solder_ball_properties: CfgSolderBallProperties = None port_properties: CfgPortProperties = None +@dataclass_json @dataclass class CfgComponents: - components: list[CfgComponent] = "default_factory" + components: list[CfgComponent] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index a40d370558..979a710178 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -1,4 +1,7 @@ from dataclasses import dataclass +import json + +from dataclasses_json import dataclass_json from pyedb.configuration.data_model.cfg_boundaries_data import CfgBoundaries from pyedb.configuration.data_model.cfg_components_data import CfgComponents @@ -17,22 +20,50 @@ from pyedb.configuration.data_model.cfg_stackup_data import CfgStackup +@dataclass_json @dataclass class Configuration: - general = CfgGeneral() - boundaries = CfgBoundaries() - nets = CfgNets() - components = CfgComponents() - pin_groups = CfgPinGroups() - sources: CfgSources - ports: CfgPorts - setups: CfgSetups - stackup: CfgStackup - padstacks: CfgPadStacks - s_parameters: CfgSparameters - spice_models: CfgSpiceModels - package_definitions: CfgPackageDefinitions - operations: CfgOperations + def __init__(self, pedb): + self._pedb = pedb + + general: CfgGeneral = None + boundaries: CfgBoundaries = None + nets: CfgNets = None + components: CfgComponents = None + pin_groups: CfgPinGroups = None + sources: CfgSources = None + ports: CfgPorts = None + setups: CfgSetups = None + stackup: CfgStackup = None + padstacks: CfgPadStacks = None + s_parameters: CfgSparameters = None + spice_models: CfgSpiceModels = None + package_definitions: CfgPackageDefinitions = None + operations: CfgOperations = None + # TODO check for variables # TODO modeler # TODO probes + + def load_file(self, file_path): + with open(file_path, "r") as file: + data = json.load(file) + self._pedb.configuration.components = CfgComponents(self._pedb).from_dict(data) + self._pedb.configuration.general = CfgGeneral().from_dict(data) + self._pedb.configuration.boundaries = CfgBoundaries().from_dict(data) + self._pedb.configuration.nets = CfgNets.from_dict(data) + self._pedb.configuration.pin_groups = CfgPinGroups().from_dict(data) + self._pedb.configuration.sources = CfgSources.from_dict(data) + self._pedb.configuration.ports = CfgPorts().from_dict(data) + self._pedb.configuration.setups = CfgSetups().from_dict(data) + self._pedb.configuration.stackup = CfgStackup().from_dict(data) + self._pedb.configuration.padstacks = CfgPadStacks().from_dict(data) + self._pedb.configuration.s_parameters = CfgSparameters().from_dict(data) + self._pedb.configuration.spice_models = CfgSpiceModels().from_dict(data) + self._pedb.configuration.package_definitions = CfgPackageDefinitions().from_dict(data) + self._pedb.configuration.operations = CfgOperations().from_dict(data) + + def load_from_layout(self, filter=None): + if not self.components: + if not self._pedb.load_configuration_from_layout(filter=filter): + raise ("Failed importing components from layout with configuration.", Exception) diff --git a/src/pyedb/configuration/data_model/cfg_general_data.py b/src/pyedb/configuration/data_model/cfg_general_data.py index 092697b781..e85df5ded4 100644 --- a/src/pyedb/configuration/data_model/cfg_general_data.py +++ b/src/pyedb/configuration/data_model/cfg_general_data.py @@ -1,19 +1,10 @@ from dataclasses import dataclass +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgGeneral: - spice_model_library: str = "" - s_parameter_library: str = "" - - def _import_from_json(self): - pass - - def _export_json(self): - pass - - def _get_from_edb(self) -> bool: - pass - - def _set_to_edb(self) -> bool: - pass + spice_model_library: str = None + s_parameter_library: str = None diff --git a/src/pyedb/configuration/data_model/cfg_nets_data.py b/src/pyedb/configuration/data_model/cfg_nets_data.py index 7a37b2800f..b00a4ca626 100644 --- a/src/pyedb/configuration/data_model/cfg_nets_data.py +++ b/src/pyedb/configuration/data_model/cfg_nets_data.py @@ -1,7 +1,10 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgNets: - power_ground_nets: list[str] = "default_factory" - signal_nets: list[str] = "default_factory" + power_ground_nets: list[str] = field(default_factory=list) + signal_nets: list[str] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_operations_data.py b/src/pyedb/configuration/data_model/cfg_operations_data.py index 6b67c0744e..4120920748 100644 --- a/src/pyedb/configuration/data_model/cfg_operations_data.py +++ b/src/pyedb/configuration/data_model/cfg_operations_data.py @@ -1,10 +1,13 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgCutout: - signal_list: [str] - reference_list: [str] + signal_list: list[str] = field(default_factory=list) + reference_list: list[str] = field(default_factory=list) extent_type: str = "ConvexHull" expansion_size: float = 0.002 use_round_corner: bool = False @@ -28,6 +31,7 @@ class CfgCutout: keep_lines_as_path: bool = False +@dataclass_json @dataclass class CfgOperations: cutout: CfgCutout = None diff --git a/src/pyedb/configuration/data_model/cfg_package_definition_data.py b/src/pyedb/configuration/data_model/cfg_package_definition_data.py index 5386234058..83c4ad8f08 100644 --- a/src/pyedb/configuration/data_model/cfg_package_definition_data.py +++ b/src/pyedb/configuration/data_model/cfg_package_definition_data.py @@ -1,27 +1,32 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgHeatSink: - fin_base_height: float - fin_height: float - fin_orientation: str - fin_spacing: float - fin_thickness: float + fin_base_height: float = 0.0 + fin_height: float = 0.0 + fin_orientation: str = str + fin_spacing: float = 0.0 + fin_thickness: float = 0.0 +@dataclass_json @dataclass class CfgPackageDefinition: - name: str - component_definition: str - maximum_power: float - therm_cond: float - theta_jb: float - theta_jc: float - height: float - heatsink: CfgHeatSink + name: str = "" + component_definition: str = "" + maximum_power: float = 0.0 + therm_cond: float = 0.0 + theta_jb: float = 0.0 + theta_jc: float = 0.0 + height: float = 0.0 + heatsink: CfgHeatSink = None +@dataclass_json @dataclass class CfgPackageDefinitions: - definitions: list[CfgPackageDefinition] = "default_factory" + definitions: list[CfgPackageDefinition] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_padsatck_data.py b/src/pyedb/configuration/data_model/cfg_padsatck_data.py index de85f12fa1..22e7ff707b 100644 --- a/src/pyedb/configuration/data_model/cfg_padsatck_data.py +++ b/src/pyedb/configuration/data_model/cfg_padsatck_data.py @@ -1,37 +1,44 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgDefinition: - name: str - hole_diameter: str - hole_plating_thickness: str - hole_material: str + name: str = "" + hole_diameter: str = "" + hole_plating_thickness: str = "" + hole_material: str = "" hole_range: str = "through" +@dataclass_json @dataclass class CfgBackDrillTop: - drill_to_layer: str - drill_diameter: str - stub_length: str + drill_to_layer: str = "" + drill_diameter: str = "" + stub_length: str = "" +@dataclass_json @dataclass class CfgBackDrillBottom: - drill_to_layer: str - drill_diameter: str - stub_length: str + drill_to_layer: str = "" + drill_diameter: str = "" + stub_length: str = "" +@dataclass_json @dataclass class CfgInstance: - name: str - backdrill_top: CfgBackDrillTop - backdrill_bottom: CfgBackDrillBottom + name: str = "" + backdrill_top: CfgBackDrillTop = None + backdrill_bottom: CfgBackDrillBottom = None +@dataclass_json @dataclass class CfgPadStacks: - definitions: list[CfgDefinition] = "default_factory" - instances: list[CfgInstance] = "default_factory" + definitions: list[CfgDefinition] = field(default_factory=list) + instances: list[CfgInstance] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_pingroup_data.py b/src/pyedb/configuration/data_model/cfg_pingroup_data.py index 3c6c4104d1..cdff3eedde 100644 --- a/src/pyedb/configuration/data_model/cfg_pingroup_data.py +++ b/src/pyedb/configuration/data_model/cfg_pingroup_data.py @@ -1,39 +1,46 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Union +from dataclasses_json import dataclass_json + +@dataclass_json() @dataclass class CfgPinGroup: - name: str - reference_designator: str - pins: list[str] - net: Union[str, list[str]] + name: str = "" + reference_designator: str = "" + pins: list[str] = field(default_factory=list) + net: Union[str, list[str]] = field(default_factory=list) +@dataclass_json @dataclass class CfgTerminal: - pingroup: str + pingroup: str = "" +@dataclass_json @dataclass class CfgPorts: - name: str - type: str - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal + name: str = "" + type: str = "" + positive_terminal: CfgTerminal = None + negative_terminal: CfgTerminal = None +@dataclass_json @dataclass class CfgSource: - name: str - type: str - magnitude: float - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal + name: str = "" + type: str = "" + magnitude: float = 0.0 + positive_terminal: CfgTerminal = None + negative_terminal: CfgTerminal = None +@dataclass_json @dataclass class CfgPinGroups: - pin_groups: list[CfgPinGroup] = "default_factory" - ports: list[CfgPorts] = "default_factory" - sources: list[CfgSource] = "default_factory" + pin_groups: list[CfgPinGroup] = field(default_factory=list) + ports: list[CfgPorts] = field(default_factory=list) + sources: list[CfgSource] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py index b3c7ac18bf..b01d5efec6 100644 --- a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py +++ b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py @@ -1,36 +1,43 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgTerminal: - pin: str - net: str + pin: str = "" + net: str = "" +@dataclass_json @dataclass class CfgPort: - name: str - reference_designator: str - type: str - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal + name: str = "" + reference_designator: str = "" + type: str = "" + positive_terminal: CfgTerminal = None + negative_terminal: CfgTerminal = None +@dataclass_json @dataclass class CfgSource: - name: str - reference_designator: str - type: str - magnitude: float - positive_terminal: CfgTerminal - negative_terminal: CfgTerminal + name: str = "" + reference_designator: str = "" + type: str = "" + magnitude: float = 0.0 + positive_terminal: CfgTerminal = None + negative_terminal: CfgTerminal = None +@dataclass_json @dataclass class CfgPorts: - ports: [CfgPort] = "default_factory" + ports: list[CfgPort] = field(default_factory=list) +@dataclass_json @dataclass class CfgSources: - sources: [CfgSource] = "default_factory" + sources: list[CfgSource] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py index 2e687d6bb2..89a04b23c6 100644 --- a/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py +++ b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py @@ -1,18 +1,22 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgSparameter: - name: str - component_definition: str - file_path: str - apply_to_all: bool - components: list[str] - reference_net: str - reference_net_per_component: dict[str, str] + name: str = "" + component_definition: str = "" + file_path: str = "" + apply_to_all: bool = True + components: list[str] = field(default_factory=list) + reference_net: str = "" + reference_net_per_component: dict[str, str] = field(default_factory=dict) +@dataclass_json @dataclass class CfgSparameters: - general: dict[str, str] = "default_factory" - s_parameters: list[CfgSparameter] = "default_factory" + general: dict[str, str] = field(default_factory=dict) + s_parameters: list[CfgSparameter] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py index b240fcc166..094a1f3cea 100644 --- a/src/pyedb/configuration/data_model/cfg_setup_data.py +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -1,39 +1,46 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgFrequency: - distribution: str - start: float - stop: float - step: float - points: int - samples: int + distribution: str = "" + start: float = 0.0 + stop: float = 0.0 + step: float = 0.0 + points: int = 0 + samples: int = 0 +@dataclass_json @dataclass class CfgFrequencySweep: - name: str - type: str - frequencies: list[CfgFrequency] + name: str = "" + type: str = "" + frequencies: list[CfgFrequency] = field(default_factory=list) +@dataclass_json @dataclass class CfgDcIrSettings: - export_dc_thermal_data: bool + export_dc_thermal_data: bool = False +@dataclass_json @dataclass class CfgSetup: - name: str - type: str - f_adapt: str - max_num_passes: int - max_mag_delta_s: float - dc_slider_position: int - dc_ir_settings: CfgDcIrSettings + name: str = "" + type: str = "" + f_adapt: str = "" + max_num_passes: int = 20 + max_mag_delta_s: float = 0.02 + dc_slider_position: int = 1 + dc_ir_settings: CfgDcIrSettings = None +@dataclass_json @dataclass class CfgSetups: - setups: list[CfgSetup] = "default_factory" + setups: list[CfgSetup] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_spice_models_data.py b/src/pyedb/configuration/data_model/cfg_spice_models_data.py index 6541d936a8..0909cf6094 100644 --- a/src/pyedb/configuration/data_model/cfg_spice_models_data.py +++ b/src/pyedb/configuration/data_model/cfg_spice_models_data.py @@ -1,22 +1,27 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgSpiceModelLib: - spice_model_library: str + spice_model_library: str = "" +@dataclass_json @dataclass class CfgSpiceModel: - name: str - component_definition: str - file_path: str - sub_circuit_name: str - apply_to_all: bool - components: [str] + name: str = "" + component_definition: str = "" + file_path: str = "" + sub_circuit_name: str = "" + apply_to_all: bool = True + components: [str] = field(default_factory=list) +@dataclass_json @dataclass class CfgSpiceModels: general: CfgSpiceModelLib = None - spice_models: list[CfgSpiceModel] = "default_factory" + spice_models: list[CfgSpiceModel] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_stackup_data.py b/src/pyedb/configuration/data_model/cfg_stackup_data.py index 6cbc1c7eb8..f6c6f64683 100644 --- a/src/pyedb/configuration/data_model/cfg_stackup_data.py +++ b/src/pyedb/configuration/data_model/cfg_stackup_data.py @@ -1,25 +1,30 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Union +from dataclasses_json import dataclass_json + +@dataclass_json @dataclass class CfgMaterial: - name: str - conductivity: float - permittivity: float - dielectric_loss_tangent: float + name: str = "" + conductivity: float = 0.0 + permittivity: float = 0.0 + dielectric_loss_tangent: float = 0.0 +@dataclass_json @dataclass class CfgLayer: - fill_material: str - material: str - name: str - thickness: Union[str, float] - type: str + fill_material: str = "" + material: str = "" + name: str = "" + thickness: Union[str, float] = "" + type: str = "" +@dataclass_json @dataclass class CfgStackup: - materials: [CfgMaterial] = "default_factory" - layers: [] = "default_factory" + materials: list[CfgMaterial] = field(default_factory=list) + layers: list = field(default_factory=list) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 0156a5fde7..9e2d2b8dd3 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -45,6 +45,12 @@ ComponentPart, Series, ) +from pyedb.configuration.data_model.cfg_components_data import ( + CfgComponent, + CfgComponents, + CfgPinPair, + CfgPinPairs, +) from pyedb.generic.general_methods import ( generate_unique_name, get_filename_without_extension, @@ -2308,3 +2314,47 @@ def create_pin_group_on_net(self, reference_designator, net_name, group_name=Non pin.name for pin in list(self.instances[reference_designator].pins.values()) if pin.net_name == net_name ] return self.create_pin_group(reference_designator, pins, group_name) + + def load_configuration_from_layout(self, filter=None) -> bool: + """Returns Components configuration class from layout. + + Parameters + ---------- + filter : list[str], optional + Provide a filter to retrieve only specific components, for instance ["capacitor", "io] will only return + `CfgComponents` object with capaitors and io components. When filter is `None` no filter is applied and + all components are returned. Default valueis `None` + + Returns + ------- + bool. `True` when succeed. + """ + if filter: + edb_components = [cmp for cmp in list(self.instances.values()) if cmp.type in filter] + else: + edb_components = [cmp for cmp in list(self.instances.values())] + if not self._pedb.configuration.components: + self._pedb.configuration.components = CfgComponents() + for _, edb_component in edb_components: + cfg_component = CfgComponent() + cfg_component.reference_designator = edb_component.refdes + cfg_component.enabled = edb_component.enabled + cfg_component.part_type = edb_component.type + if edb_component.type == "io": + pass + if edb_component.type in ["capacitor", "inductor", "resistor"]: + cfg_component.rlc_model = CfgPinPairs() + for pin_pair in edb_component.model.pin_pairs(): + cfg_pin_pair = CfgPinPair() + cfg_pin_pair.p1 = pin_pair[0] + cfg_pin_pair.p2 = pin_pair[1] + if edb_component.is_parallel_rlc: + cfg_pin_pair.type = "parallel" + else: + cfg_pin_pair.type = "series" + cfg_pin_pair.resistance = edb_component.res_value + cfg_pin_pair.inductance = edb_component.ind_value + cfg_pin_pair.capacitance = edb_component.cap_value + cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) + self._pedb.configuration.components.append(cfg_component) + return True From 32ec8704463ca7343cd0a5bf27fdfce6f2e17e33 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Fri, 30 May 2025 16:33:49 +0200 Subject: [PATCH 08/27] cfg data model load components from layout --- .../data_model/cfg_configuration_data.py | 5 ++-- src/pyedb/grpc/database/components.py | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index 979a710178..b499c9d729 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -64,6 +64,5 @@ def load_file(self, file_path): self._pedb.configuration.operations = CfgOperations().from_dict(data) def load_from_layout(self, filter=None): - if not self.components: - if not self._pedb.load_configuration_from_layout(filter=filter): - raise ("Failed importing components from layout with configuration.", Exception) + if not self._pedb.components.load_configuration_from_layout(filter=filter): + raise ("Failed importing components from layout with configuration.", Exception) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 9e2d2b8dd3..ef22f7ac78 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -50,6 +50,8 @@ CfgComponents, CfgPinPair, CfgPinPairs, + CfgPortProperties, + CfgSolderBallProperties, ) from pyedb.generic.general_methods import ( generate_unique_name, @@ -2341,8 +2343,25 @@ def load_configuration_from_layout(self, filter=None) -> bool: cfg_component.enabled = edb_component.enabled cfg_component.part_type = edb_component.type if edb_component.type == "io": - pass - if edb_component.type in ["capacitor", "inductor", "resistor"]: + cfg_solder_ball_properties = CfgSolderBallProperties() + cfg_solder_ball_properties.shape = edb_component.solder_ball_shape + cfg_solder_ball_properties.height = edb_component.solder_ball_height + cfg_solder_ball_properties.diameter = edb_component.solder_ball_diameter[0] + cfg_component.solder_ball_properties = cfg_solder_ball_properties + + cfg_port_properties = CfgPortProperties() + cfg_port_properties.reference_size_auto = ( + edb_component.component_property.port_property.reference_size_auto + ) + cfg_port_properties.reference_size_x = ( + edb_component.component_property.port_property.get_reference_size()[0] + ) + cfg_port_properties.reference_size_y = ( + edb_component.component_property.port_property.get_reference_size()[1] + ) + cfg_port_properties.reference_offset = edb_component.component_property.port_property.reference_height + cfg_component.port_properties = cfg_port_properties + cfg_component.rlc_model = CfgPinPairs() for pin_pair in edb_component.model.pin_pairs(): cfg_pin_pair = CfgPinPair() From 01f19b5cd775cc148ed6f80e6ca148478eb9f1cc Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Fri, 30 May 2025 16:34:49 +0200 Subject: [PATCH 09/27] cfg data model load components from layout --- src/pyedb/grpc/database/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index ef22f7ac78..b7e9d64588 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -2337,7 +2337,7 @@ def load_configuration_from_layout(self, filter=None) -> bool: edb_components = [cmp for cmp in list(self.instances.values())] if not self._pedb.configuration.components: self._pedb.configuration.components = CfgComponents() - for _, edb_component in edb_components: + for edb_component in edb_components: cfg_component = CfgComponent() cfg_component.reference_designator = edb_component.refdes cfg_component.enabled = edb_component.enabled From c247a181d855809e7e6fdf82264b500f47bd31a8 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Fri, 30 May 2025 16:48:51 +0200 Subject: [PATCH 10/27] cfg data model load nets from layout --- .../data_model/cfg_configuration_data.py | 2 ++ src/pyedb/grpc/database/nets.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index b499c9d729..93f636baad 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -66,3 +66,5 @@ def load_file(self, file_path): def load_from_layout(self, filter=None): if not self._pedb.components.load_configuration_from_layout(filter=filter): raise ("Failed importing components from layout with configuration.", Exception) + if not self._pedb.nets.load_configuration_from_layout(filter=filter): + raise ("Failed importing nets from layout with configuration.", Exception) diff --git a/src/pyedb/grpc/database/nets.py b/src/pyedb/grpc/database/nets.py index 77bf43a9ca..59660fcdda 100644 --- a/src/pyedb/grpc/database/nets.py +++ b/src/pyedb/grpc/database/nets.py @@ -25,6 +25,7 @@ import warnings from pyedb.common.nets import CommonNets +from pyedb.configuration.data_model.cfg_nets_data import CfgNets from pyedb.generic.general_methods import generate_unique_name from pyedb.grpc.database.net.net import Net from pyedb.grpc.database.primitive.bondwire import Bondwire @@ -631,3 +632,28 @@ def merge_nets_polygons(self, net_names_list): if isinstance(net_names_list, str): net_names_list = [net_names_list] return self._pedb.modeler.unite_polygons_on_layer(net_names_list=net_names_list) + + def load_configuration_from_layout(self, filter=None) -> bool: + """Update nets from layout inn configuration. + + Parameters + ---------- + filter : list[str], optional + Provide a filter to retrieve only specific nets, for instance ["GND", "net1"] will only return + The nets included in the list. When filter is `None` no filter is applied and all nets are returned. + Default valueis `None` + + Returns + ------- + bool. `True` when succeed. + """ + if not self._pedb.configuration.components: + self._pedb.configuration.nets = CfgNets() + signal_nets = list(self.nets.signal.keys()) + power_nets = list(self.nets.power.keys()) + if filter: + signal_nets = [net for net in signal_nets if net in filter] + power_nets = [net for net in power_nets if net in filter] + self._pedb.configuration.nets.signal_nets = signal_nets + self._pedb.configuration.nets.power = power_nets + return True From 2e20c0952a3a47b07d8bb66001165d58f3173004 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Fri, 30 May 2025 17:07:01 +0200 Subject: [PATCH 11/27] cfg data model load nets from layout --- .../configuration/data_model/cfg_configuration_data.py | 4 ++++ src/pyedb/grpc/database/components.py | 10 +++++----- src/pyedb/grpc/database/nets.py | 8 ++++---- src/pyedb/grpc/edb.py | 6 ++++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index 93f636baad..d9cba706a4 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -64,7 +64,11 @@ def load_file(self, file_path): self._pedb.configuration.operations = CfgOperations().from_dict(data) def load_from_layout(self, filter=None): + self._pedb.logger.info("Loading components") if not self._pedb.components.load_configuration_from_layout(filter=filter): raise ("Failed importing components from layout with configuration.", Exception) + self._pedb.logger.info("Done") + self._pedb.logger.info("Loading nets") if not self._pedb.nets.load_configuration_from_layout(filter=filter): raise ("Failed importing nets from layout with configuration.", Exception) + self._pedb.logger.info("Done") diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index b7e9d64588..d092ca1fa0 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -2361,7 +2361,7 @@ def load_configuration_from_layout(self, filter=None) -> bool: ) cfg_port_properties.reference_offset = edb_component.component_property.port_property.reference_height cfg_component.port_properties = cfg_port_properties - + elif edb_component.type in ["resistor", "inductor", "capacitor"]: cfg_component.rlc_model = CfgPinPairs() for pin_pair in edb_component.model.pin_pairs(): cfg_pin_pair = CfgPinPair() @@ -2371,9 +2371,9 @@ def load_configuration_from_layout(self, filter=None) -> bool: cfg_pin_pair.type = "parallel" else: cfg_pin_pair.type = "series" - cfg_pin_pair.resistance = edb_component.res_value - cfg_pin_pair.inductance = edb_component.ind_value - cfg_pin_pair.capacitance = edb_component.cap_value + cfg_pin_pair.resistance = round(edb_component.res_value, 15) + cfg_pin_pair.inductance = round(edb_component.ind_value, 15) + cfg_pin_pair.capacitance = round(edb_component.cap_value, 15) cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) - self._pedb.configuration.components.append(cfg_component) + self._pedb.configuration.components.components.append(cfg_component) return True diff --git a/src/pyedb/grpc/database/nets.py b/src/pyedb/grpc/database/nets.py index 59660fcdda..36868a42ca 100644 --- a/src/pyedb/grpc/database/nets.py +++ b/src/pyedb/grpc/database/nets.py @@ -647,13 +647,13 @@ def load_configuration_from_layout(self, filter=None) -> bool: ------- bool. `True` when succeed. """ - if not self._pedb.configuration.components: + if not self._pedb.configuration.nets: self._pedb.configuration.nets = CfgNets() - signal_nets = list(self.nets.signal.keys()) - power_nets = list(self.nets.power.keys()) + signal_nets = list(self.signal.keys()) + power_nets = list(self.power.keys()) if filter: signal_nets = [net for net in signal_nets if net in filter] power_nets = [net for net in power_nets if net in filter] self._pedb.configuration.nets.signal_nets = signal_nets - self._pedb.configuration.nets.power = power_nets + self._pedb.configuration.nets.power_ground_nets = power_nets return True diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index 061f2ff8ca..b83696e87a 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -47,7 +47,9 @@ from ansys.edb.core.utility.value import Value as GrpcValue import rtree -from pyedb.configuration.configuration import Configuration +from pyedb.configuration.data_model.cfg_configuration_data import ( + Configuration as ConfigurationData, +) from pyedb.generic.constants import unit_converter from pyedb.generic.general_methods import ( generate_unique_name, @@ -855,7 +857,7 @@ def configuration(self): :class:`Configuration `. """ if not self._configuration: - self._configuration = Configuration(self) + self._configuration = ConfigurationData(self) return self._configuration def edb_exception(self, ex_value, tb_data): From bdc7baf5ed910f0bd7566104292cb44f83a76e7a Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Sat, 31 May 2025 13:44:39 +0200 Subject: [PATCH 12/27] grpc edb output --- .../data_model/cfg_configuration_data.py | 5 + src/pyedb/grpc/database/components.py | 17 +- src/pyedb/grpc/edb.py | 176 ++++++++++-------- 3 files changed, 113 insertions(+), 85 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index d9cba706a4..7dbb4c4e36 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -63,6 +63,11 @@ def load_file(self, file_path): self._pedb.configuration.package_definitions = CfgPackageDefinitions().from_dict(data) self._pedb.configuration.operations = CfgOperations().from_dict(data) + def export_configuration_file(self, file_path): + data = self._pedb.configuration.to_dict() + with open(file_path, "w") as file: + json.dump(data, file, indent=4) + def load_from_layout(self, filter=None): self._pedb.logger.info("Loading components") if not self._pedb.components.load_configuration_from_layout(filter=filter): diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index d092ca1fa0..53e7d54745 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -2324,8 +2324,8 @@ def load_configuration_from_layout(self, filter=None) -> bool: ---------- filter : list[str], optional Provide a filter to retrieve only specific components, for instance ["capacitor", "io] will only return - `CfgComponents` object with capaitors and io components. When filter is `None` no filter is applied and - all components are returned. Default valueis `None` + `CfgComponents` object with capacitors and io components. When filter is `None`, no filter is applied and + all components are returned. Default value is `None` Returns ------- @@ -2354,12 +2354,14 @@ def load_configuration_from_layout(self, filter=None) -> bool: edb_component.component_property.port_property.reference_size_auto ) cfg_port_properties.reference_size_x = ( - edb_component.component_property.port_property.get_reference_size()[0] + edb_component.component_property.port_property.get_reference_size()[0].value ) cfg_port_properties.reference_size_y = ( - edb_component.component_property.port_property.get_reference_size()[1] + edb_component.component_property.port_property.get_reference_size()[1].value + ) + cfg_port_properties.reference_offset = ( + edb_component.component_property.port_property.reference_height.value ) - cfg_port_properties.reference_offset = edb_component.component_property.port_property.reference_height cfg_component.port_properties = cfg_port_properties elif edb_component.type in ["resistor", "inductor", "capacitor"]: cfg_component.rlc_model = CfgPinPairs() @@ -2377,3 +2379,8 @@ def load_configuration_from_layout(self, filter=None) -> bool: cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) self._pedb.configuration.components.components.append(cfg_component) return True + + def apply_configuration_to_layout(self) -> bool: + for cfg_cmp in self._pedb.configuration.components.components: + edb_component = self.instances[cfg_cmp.reference_designator] + return True diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index b83696e87a..ef5f13bfc1 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -420,17 +420,17 @@ def _init_objects(self): self._extended_nets = ExtendedNets(self) @property - def cell_names(self): + def cell_names(self) -> list[str]: """Cell name container. Returns ------- - list of cell names : List[str] + list of cell names : list[str] """ return [cell.name for cell in self.active_db.top_circuit_cells] @property - def design_variables(self): + def design_variables(self) -> dict[str, float]: """Get all edb design variables. Returns @@ -440,7 +440,7 @@ def design_variables(self): return {i: self.active_cell.get_variable_value(i).value for i in self.active_cell.get_all_variable_names()} @property - def project_variables(self): + def project_variables(self) -> dict[str, float]: """Get all project variables. Returns @@ -451,7 +451,7 @@ def project_variables(self): return {i: self.active_db.get_variable_value(i).value for i in self.active_db.get_all_variable_names()} @property - def layout_validation(self): + def layout_validation(self) -> LayoutValidation: """Return LayoutValidation object. Returns @@ -461,7 +461,7 @@ def layout_validation(self): return LayoutValidation(self) @property - def variables(self): + def variables(self) -> dict[str, float]: """Get all Edb variables. Returns @@ -477,7 +477,7 @@ def variables(self): return all_vars @property - def terminals(self): + def terminals(self) -> dict[str, Terminal]: """Get terminals belonging to active layout. Returns @@ -487,7 +487,7 @@ def terminals(self): return {i.name: i for i in self.layout.terminals} @property - def excitations(self): + def excitations(self) -> dict[str, Union[BundleWavePort, GapPort]]: """Get all layout excitations. Returns @@ -505,7 +505,7 @@ def excitations(self): return temp @property - def ports(self): + def ports(self) -> dict[str, Terminal]: """Get all ports. Returns @@ -539,7 +539,7 @@ def ports(self): return ports @property - def excitations_nets(self): + def excitations_nets(self) -> list[str]: """Get all net names with excitation defined. Returns @@ -550,7 +550,7 @@ def excitations_nets(self): return list(set([i.net.name for i in self.layout.terminals if not i.is_reference_terminal])) @property - def sources(self): + def sources(self) -> dict[str, Terminal]: """Get all layout sources. Returns @@ -576,7 +576,7 @@ def voltage_regulator_modules(self): return _vrms @property - def probes(self): + def probes(self) -> dict[str, Terminal]: """Get all layout probes. Returns @@ -587,7 +587,7 @@ def probes(self): terms = [term for term in self.layout.terminals if term.boundary_type.value == 8] return {ter.name: ter for ter in terms} - def open_edb(self, restart_rpc_server=False, kill_all_instances=False): + def open_edb(self, restart_rpc_server=False) -> bool: """Open EDB. Returns @@ -628,7 +628,7 @@ def open_edb(self, restart_rpc_server=False, kill_all_instances=False): self.logger.error("Builder was not initialized.") return True - def create_edb(self, restart_rpc_server=False, kill_all_instances=False): + def create_edb(self, restart_rpc_server=False) -> bool: """Create EDB. Returns @@ -647,8 +647,8 @@ def create_edb(self, restart_rpc_server=False, kill_all_instances=False): except Exception as e: self.logger.error(e.args[0]) if not self.db: - raise ValueError("Failed creating EDB.") self._active_cell = None + raise ValueError("Failed creating EDB.") else: if not self.cellname: self.cellname = generate_unique_name("Cell") @@ -803,7 +803,7 @@ def import_layout_file( self.edbpath = os.path.join(working_dir, aedb_name) return self.open_edb() - def export_to_ipc2581(self, ipc_path=None, units="MILLIMETER"): + def export_to_ipc2581(self, ipc_path=None, units="MILLIMETER") -> str: """Create an XML IPC2581 file from the active EDB. .. note:: @@ -849,7 +849,7 @@ def export_to_ipc2581(self, ipc_path=None, units="MILLIMETER"): return False @property - def configuration(self): + def configuration(self) -> ConfigurationData: """Edb project configuration from a file. Returns @@ -919,7 +919,7 @@ def active_cell(self, value): raise "No valid design." @property - def components(self): + def components(self) -> Components: """Edb Components methods and properties. Returns @@ -937,7 +937,7 @@ def components(self): return self._components @property - def stackup(self): + def stackup(self) -> Stackup: """Stackup manager. Returns @@ -957,7 +957,7 @@ def stackup(self): return self._stackup @property - def source_excitation(self): + def source_excitation(self) -> SourceExcitation: """Returns layout source excitations. Returns @@ -968,7 +968,7 @@ def source_excitation(self): return self._source_excitation @property - def materials(self): + def materials(self) -> Materials: """Material Database. Returns @@ -988,7 +988,7 @@ def materials(self): return self._materials @property - def padstacks(self): + def padstacks(self) -> Padstacks: """Returns padstack object. @@ -1011,7 +1011,7 @@ def padstacks(self): return self._padstack @property - def siwave(self): + def siwave(self) -> Siwave: """Returns SIWave object. Returns @@ -1029,7 +1029,7 @@ def siwave(self): return self._siwave @property - def hfss(self): + def hfss(self) -> Hfss: """Returns HFSS object. Returns @@ -1049,7 +1049,7 @@ def hfss(self): return self._hfss @property - def nets(self): + def nets(self) -> Nets: """Returns nets object. Returns @@ -1069,7 +1069,7 @@ def nets(self): return self._nets @property - def net_classes(self): + def net_classes(self) -> NetClass: """Returns net classes object. Returns @@ -1087,7 +1087,7 @@ def net_classes(self): return {net.name: NetClass(self, net) for net in self.active_layout.net_classes} @property - def extended_nets(self): + def extended_nets(self) -> ExtendedNets: """Returns extended nets. Returns @@ -1106,7 +1106,7 @@ def extended_nets(self): return self._extended_nets @property - def differential_pairs(self): + def differential_pairs(self) -> DifferentialPairs: """Returns differential pairs. Returns @@ -1124,7 +1124,7 @@ def differential_pairs(self): return self._differential_pairs @property - def modeler(self): + def modeler(self) -> Modeler: """Returns primitives modeler object. Returns @@ -1142,7 +1142,7 @@ def modeler(self): return self._modeler @property - def layout(self): + def layout(self) -> Layout: """Returns Layout object. Returns @@ -1152,7 +1152,7 @@ def layout(self): return Layout(self) @property - def active_layout(self): + def active_layout(self) -> Layout: """Active layout. Returns @@ -1173,7 +1173,9 @@ def layout_instance(self): self._layout_instance = self.layout.layout_instance return self._layout_instance - def get_connected_objects(self, layout_object_instance): + def get_connected_objects( + self, layout_object_instance + ) -> list[Union[PadstackInstance, Path, Rectangle, Circle, Polygon]]: """Returns connected objects. Returns @@ -1257,7 +1259,7 @@ def point_data(self, x, y=None): return PointData(x, y) @staticmethod - def _is_file_existing_and_released(filename): + def _is_file_existing_and_released(filename) -> bool: if os.path.exists(filename): try: os.rename(filename, filename + "_") @@ -1269,13 +1271,13 @@ def _is_file_existing_and_released(filename): return False @staticmethod - def _is_file_existing(filename): + def _is_file_existing(filename) -> bool: if os.path.exists(filename): return True else: return False - def _wait_for_file_release(self, timeout=30, file_to_release=None): + def _wait_for_file_release(self, timeout=30, file_to_release=None) -> bool: if not file_to_release: file_to_release = os.path.join(self.edbpath) tstart = time.time() @@ -1305,7 +1307,7 @@ def _wait_for_file_exists(self, timeout=30, file_to_release=None, wait_count=4): times = 0 time.sleep(0.250) - def close_edb(self): + def close_edb(self) -> bool: """Close EDB and cleanup variables. Returns @@ -1320,7 +1322,7 @@ def close_edb(self): self._clean_variables() return True - def save_edb(self): + def save_edb(self) -> bool: """Save the EDB file. Returns @@ -1334,7 +1336,7 @@ def save_edb(self): self.logger.info("EDB file save time: {0:.2f}ms".format(elapsed_time * 1000.0)) return True - def save_edb_as(self, fname): + def save_edb_as(self, fname) -> bool: """Save the EDB file as another file. Parameters @@ -1373,7 +1375,7 @@ def execute(self, func): # return self.edb_api.utility.utility.Command.Execute(func) pass - def import_cadence_file(self, inputBrd, WorkDir=None, anstranslator_full_path="", use_ppe=False): + def import_cadence_file(self, inputBrd, WorkDir=None, anstranslator_full_path="", use_ppe=False) -> bool: """Import a board file and generate an ``edb.def`` file in the working directory. Parameters @@ -1412,7 +1414,7 @@ def import_gds_file( tech_file=None, map_file=None, layer_filter=None, - ): + ) -> bool: """Import a GDS file and generate an ``edb.def`` file in the working directory. ..note:: @@ -1505,7 +1507,7 @@ def _create_extent( include_pingroups=True, pins_to_preserve=None, inlcude_voids_in_extents=False, - ): + ) -> GrpcPolygonData: from ansys.edb.core.geometry.polygon_data import ExtentType as GrpcExtentType if extent_type in [ @@ -1584,7 +1586,7 @@ def _create_conformal( reference_list=[], pins_to_preserve=None, inlcude_voids_in_extents=False, - ): + ) -> GrpcPolygonData: names = [] _polys = [] for net in net_signals: @@ -1654,7 +1656,7 @@ def _create_conformal( areas = [i.area() for i in _poly_unite] return _poly_unite[areas.index(max(areas))] - def _smart_cut(self, reference_list=[], expansion_size=1e-12): + def _smart_cut(self, reference_list=[], expansion_size=1e-12) -> list[GrpcPolygonData]: from ansys.edb.core.geometry.point_data import PointData as GrpcPointData _polys = [] @@ -1684,7 +1686,7 @@ def _create_convex_hull( smart_cut=False, reference_list=[], pins_to_preserve=None, - ): + ) -> GrpcPolygonData: names = [] _polys = [] for net in net_signals: @@ -1738,7 +1740,7 @@ def cutout( simple_pad_check=True, keep_lines_as_path=False, include_voids_in_extents=False, - ): + ) -> list[float]: """Create a cutout using an approach entirely based on PyAEDT. This method replaces all legacy cutout methods in PyAEDT. It does in sequence: @@ -1976,7 +1978,7 @@ def _create_cutout_legacy( check_terminals=False, include_pingroups=True, inlcude_voids_in_extents=False, - ): + ) -> list[list[float]]: expansion_size = GrpcValue(expansion_size).value # validate nets in layout @@ -2308,7 +2310,7 @@ def pins_clean(pinst): self.logger.reset_timer() return [[pt.x.value, pt.y.value] for pt in _poly.without_arcs().points] - def get_conformal_polygon_from_netlist(self, netlist=None): + def get_conformal_polygon_from_netlist(self, netlist=None) -> GrpcPolygonData: """Returns conformal polygon data based on a netlist. Parameters @@ -2339,7 +2341,7 @@ def get_conformal_polygon_from_netlist(self, netlist=None): else: return False - def number_with_units(self, value, units=None): + def number_with_units(self, value, units=None) -> str: """Convert a number to a string with units. If value is a string, it's returned as is. Parameters @@ -2525,7 +2527,7 @@ def _create_cutout_on_point_list( return [[pt.x.value, pt.y.value] for pt in polygon_data.without_arcs().points] @staticmethod - def write_export3d_option_config_file(path_to_output, config_dictionaries=None): + def write_export3d_option_config_file(path_to_output, config_dictionaries=None) -> str: """Write the options for a 3D export to a configuration file. Parameters @@ -2536,6 +2538,10 @@ def write_export3d_option_config_file(path_to_output, config_dictionaries=None): config_dictionaries : dict, optional Configuration dictionaries. The default is ``None``. + Returns + ------- + str + Configuration file path. """ option_config = { "UNITE_NETS": 1, @@ -2569,7 +2575,7 @@ def export_hfss( num_cores=None, aedt_file_name=None, hidden=False, - ): + ) -> str: """Export EDB to HFSS. Parameters @@ -2612,7 +2618,7 @@ def export_q3d( num_cores=None, aedt_file_name=None, hidden=False, - ): + ) -> str: """Export EDB to Q3D. Parameters @@ -2662,7 +2668,7 @@ def export_maxwell( num_cores=None, aedt_file_name=None, hidden=False, - ): + ) -> str: """Export EDB to Maxwell 3D. Parameters @@ -2706,7 +2712,7 @@ def export_maxwell( hidden=hidden, ) - def solve_siwave(self): + def solve_siwave(self) -> str: """Close EDB and solve it with Siwave. Returns @@ -2784,7 +2790,7 @@ def export_siwave_dc_results( hidden=True, ) - def variable_exists(self, variable_name): + def variable_exists(self, variable_name) -> bool: """Check if a variable exists or not. Returns @@ -2802,7 +2808,7 @@ def variable_exists(self, variable_name): return True return False - def get_variable(self, variable_name): + def get_variable(self, variable_name) -> float: """Return Variable Value if variable exists. Parameters @@ -2823,7 +2829,7 @@ def get_variable(self, variable_name): self.logger.info(f"Variable {variable_name} doesn't exists.") return False - def add_project_variable(self, variable_name, variable_value, description=None): + def add_project_variable(self, variable_name, variable_value, description=None) -> bool: """Add a variable to database. The variable will have the prefix `$`. Parameters @@ -2852,14 +2858,15 @@ def add_project_variable(self, variable_name, variable_value, description=None): if not variable_name.startswith("$"): variable_name = f"${variable_name}" if not self.variable_exists(variable_name): - var = self.active_db.add_variable(variable_name, variable_value) + self.active_db.add_variable(variable_name, variable_value) if description: self.active_db.set_variable_desc(name=variable_name, desc=description) + return True else: self.logger.error(f"Variable {variable_name} already exists.") return False - def add_design_variable(self, variable_name, variable_value, is_parameter=False, description=None): + def add_design_variable(self, variable_name, variable_value, is_parameter=False, description=None) -> bool: """Add a variable to edb. The variable can be a design one or a project variable (using ``$`` prefix). Parameters @@ -2895,15 +2902,15 @@ def add_design_variable(self, variable_name, variable_value, is_parameter=False, if variable_name.startswith("$"): variable_name = variable_name[1:] if not self.variable_exists(variable_name): - var = self.active_cell.add_variable(variable_name, variable_value) + self.active_cell.add_variable(variable_name, variable_value) if description: self.active_cell.set_variable_desc(name=variable_name, desc=description) - return var + return True else: self.logger.error(f"Variable {variable_name} already exists.") return False - def change_design_variable_value(self, variable_name, variable_value): + def change_design_variable_value(self, variable_name, variable_value) -> bool: """Change a variable value. Parameters @@ -2929,10 +2936,13 @@ def change_design_variable_value(self, variable_name, variable_value): if self.variable_exists(variable_name): if variable_name in self.db.get_all_variable_names(): self.db.set_variable_value(variable_name, GrpcValue(variable_value)) + return True elif variable_name in self.active_cell.get_all_variable_names(): self.active_cell.set_variable_value(variable_name, GrpcValue(variable_value)) + return True + return False - def get_bounding_box(self): + def get_bounding_box(self) -> list[list[float]]: """Get the layout bounding box. Returns @@ -2953,7 +2963,7 @@ def get_statistics(self, compute_area=False): """ return self.modeler.get_layout_statistics(evaluate_area=compute_area, net_list=None) - def are_port_reference_terminals_connected(self, common_reference=None): + def are_port_reference_terminals_connected(self, common_reference=None) -> bool: """Check if all terminal references in design are connected. If the reference nets are different, there is no hope for the terminal references to be connected. After we have identified a common reference net we need to loop the terminals again to get @@ -3041,7 +3051,9 @@ def are_port_reference_terminals_connected(self, common_reference=None): return True if len(iDintersection) > 0 else False @property - def setups(self): + def setups( + self, + ) -> Union[HfssSimulationSetup, SiwaveSimulationSetup, SIWaveDCIRSimulationSetup, RaptorXSimulationSetup]: """Get the dictionary of all EDB HFSS and SIwave setups. Returns @@ -3083,7 +3095,7 @@ def hfss_setups(self): return setups @property - def siwave_dc_setups(self): + def siwave_dc_setups(self) -> dict[str, SIWaveDCIRSimulationSetup]: """Active Siwave DC IR Setups. Returns @@ -3095,7 +3107,7 @@ def siwave_dc_setups(self): return {name: i for name, i in self.setups.items() if isinstance(i, SIWaveDCIRSimulationSetup)} @property - def siwave_ac_setups(self): + def siwave_ac_setups(self) -> dict[str, SiwaveSimulationSetup]: """Active Siwave SYZ setups. Returns @@ -3105,7 +3117,9 @@ def siwave_ac_setups(self): """ return {name: i for name, i in self.setups.items() if isinstance(i, SiwaveSimulationSetup)} - def create_hfss_setup(self, name=None, start_frequency="0GHz", stop_frequency="20GHz", step_frequency="10MHz"): + def create_hfss_setup( + self, name=None, start_frequency="0GHz", stop_frequency="20GHz", step_frequency="10MHz" + ) -> HfssSimulationSetup: """Create an HFSS simulation setup from a template. . deprecated:: pyedb 0.30.0 @@ -3133,7 +3147,7 @@ def create_hfss_setup(self, name=None, start_frequency="0GHz", stop_frequency="2 step_freq=step_frequency, ) - def create_raptorx_setup(self, name=None): + def create_raptorx_setup(self, name=None) -> RaptorXSimulationSetup: """Create an RaptorX simulation setup from a template. Parameters @@ -3187,7 +3201,7 @@ def create_hfsspi_setup(self, name=None): # TODO check HFSS-PI with Grpc. seems to defined at terminal level not setup. pass - def create_siwave_syz_setup(self, name=None, **kwargs): + def create_siwave_syz_setup(self, name=None, **kwargs) -> SiwaveSimulationSetup: """Create a setup from a template. Parameters @@ -3224,7 +3238,7 @@ def create_siwave_syz_setup(self, name=None, **kwargs): setattr(setup, k, v) return self.setups[name] - def create_siwave_dc_setup(self, name=None, **kwargs): + def create_siwave_dc_setup(self, name=None, **kwargs) -> SIWaveDCIRSimulationSetup: """Create a setup from a template. Parameters @@ -3254,7 +3268,7 @@ def create_siwave_dc_setup(self, name=None, **kwargs): setattr(setup, k, v) return setup - def calculate_initial_extent(self, expansion_factor): + def calculate_initial_extent(self, expansion_factor) -> float: """Compute a float representing the larger number between the dielectric thickness or trace width multiplied by the nW factor. The trace width search is limited to nets with ports attached. @@ -3286,7 +3300,7 @@ def calculate_initial_extent(self, expansion_factor): self.logger.info(f"The W factor is {expansion_factor}, The initial extent = {max_width}") return max_width - def copy_zones(self, working_directory=None): + def copy_zones(self, working_directory=None) -> list[str]: """Copy multizone EDB project to one new edb per zone. Parameters @@ -3467,7 +3481,9 @@ def _get_connected_ports_from_multizone_cutout(terminal_info_dict): connected_ports_list.append((port1_connexion, port2_connexion)) return connected_ports_list - def create_port(self, terminal, ref_terminal=None, is_circuit_port=False, name=None): + def create_port( + self, terminal, ref_terminal=None, is_circuit_port=False, name=None + ) -> Union[GapPort, WavePort, CoaxPort, BundleWavePort]: """Create a port. Parameters @@ -3505,7 +3521,7 @@ def create_port(self, terminal, ref_terminal=None, is_circuit_port=False, name=N terminal.name = name return self.ports[terminal.name] - def create_voltage_probe(self, terminal, ref_terminal): + def create_voltage_probe(self, terminal, ref_terminal) -> Terminal: """Create a voltage probe. Parameters @@ -3534,7 +3550,7 @@ def create_voltage_probe(self, terminal, ref_terminal): term.ref_terminal = ref_terminal return term - def create_voltage_source(self, terminal, ref_terminal): + def create_voltage_source(self, terminal, ref_terminal) -> Terminal: """Create a voltage source. Parameters @@ -3563,7 +3579,7 @@ def create_voltage_source(self, terminal, ref_terminal): term.ref_terminal = ref_terminal return term - def create_current_source(self, terminal, ref_terminal): + def create_current_source(self, terminal, ref_terminal) -> Terminal: """Create a current source. Parameters @@ -3633,7 +3649,7 @@ def auto_parametrize_design( expand_polygons_size=0, expand_voids_size=0, via_offset=True, - ): + ) -> list[str]: """Assign automatically design and project variables with current values. Parameters @@ -3904,7 +3920,7 @@ def create_model_for_arbitrary_wave_ports( terminal_diameter=None, output_edb=None, launching_box_thickness="100um", - ): + ) -> bool: """Generate EDB design to be consumed by PyAEDT to generate arbitrary wave ports shapes. This model has to be considered as merged onto another one. The current opened design must have voids surrounding the pad-stacks where wave ports terminal will be created. THe open design won't be edited, only @@ -4071,7 +4087,7 @@ def definitions(self): return Definitions(self) @property - def workflow(self): + def workflow(self) -> Workflow: """Returns workflow class. Returns @@ -4080,7 +4096,7 @@ def workflow(self): """ return Workflow(self) - def export_gds_comp_xml(self, comps_to_export, gds_comps_unit="mm", control_path=None): + def export_gds_comp_xml(self, comps_to_export, gds_comps_unit="mm", control_path=None) -> bool: """Exports an XML file with selected components information for use in a GDS import. Parameters ---------- From ba484e2314841c349169439890fbeeda8bd80863 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Sat, 31 May 2025 14:04:02 +0200 Subject: [PATCH 13/27] grpc components output --- src/pyedb/grpc/database/components.py | 92 +++++++++++++-------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 53e7d54745..de3502cace 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -156,7 +156,7 @@ def _db(self): return self._pedb.active_db @property - def instances(self): + def instances(self) -> dict[str, Component]: """All Cell components objects. Returns @@ -175,7 +175,7 @@ def instances(self): return self._cmp @property - def definitions(self): + def definitions(self) -> dict[str, ComponentDef]: """Retrieve component definition list. Returns @@ -184,11 +184,11 @@ def definitions(self): return {l.name: ComponentDef(self._pedb, l) for l in self._pedb.component_defs} @property - def nport_comp_definition(self): + def nport_comp_definition(self) -> dict[str, ComponentDef]: """Retrieve Nport component definition list.""" return {name: l for name, l in self.definitions.items() if l.reference_file} - def import_definition(self, file_path): + def import_definition(self, file_path) -> bool: """Import component definition from json file. Parameters @@ -222,7 +222,7 @@ def import_definition(self, file_path): pass return True - def export_definition(self, file_path): + def export_definition(self, file_path) -> str: """Export component definitions to json file. Parameters @@ -272,7 +272,7 @@ def export_definition(self, file_path): json.dump(data, f, ensure_ascii=False, indent=4) return file_path - def refresh_components(self): + def refresh_components(self) -> bool: """Refresh the component dictionary.""" self._logger.info("Refreshing the Components dictionary.") self._cmp = {} @@ -307,7 +307,7 @@ def refresh_components(self): return True @property - def resistors(self): + def resistors(self) -> dict[str, Component]: """Resistors. Returns @@ -325,7 +325,7 @@ def resistors(self): return self._res @property - def capacitors(self): + def capacitors(self) -> dict[str, Component]: """Capacitors. Returns @@ -343,7 +343,7 @@ def capacitors(self): return self._cap @property - def inductors(self): + def inductors(self) -> dict[str, Component]: """Inductors. Returns @@ -362,7 +362,7 @@ def inductors(self): return self._ind @property - def ICs(self): + def ICs(self) -> dict[str, Component]: """Integrated circuits. Returns @@ -381,7 +381,7 @@ def ICs(self): return self._ics @property - def IOs(self): + def IOs(self) -> dict[str, Component]: """Circuit inupts and outputs. Returns @@ -400,7 +400,7 @@ def IOs(self): return self._ios @property - def Others(self): + def Others(self) -> dict[str, Component]: """Other core components. Returns @@ -419,7 +419,7 @@ def Others(self): return self._others @property - def components_by_partname(self): + def components_by_partname(self) -> dict[str, Component]: """Components by part name. Returns @@ -443,7 +443,7 @@ def components_by_partname(self): self._comps_by_part[val.partname] = [val] return self._comps_by_part - def get_component_by_name(self, name): + def get_component_by_name(self, name) -> Component: """Retrieve a component by name. Parameters @@ -487,7 +487,7 @@ def get_pin_from_component(self, component, net_name=None, pin_name=None): pins = [pin for pin in pins if pin.name == pin_name] return pins - def get_components_from_nets(self, netlist=None): + def get_components_from_nets(self, netlist=None) -> list[str]: """Retrieve components from a net list. Parameters @@ -617,7 +617,7 @@ def get_component_placement_vector( self._logger.warning("Failed to compute vector.") return False, [0, 0], 0, 0 - def get_solder_ball_height(self, cmp): + def get_solder_ball_height(self, cmp) -> float: """Get component solder ball height. Parameters @@ -635,7 +635,7 @@ def get_solder_ball_height(self, cmp): cmp = self.get_component_by_name(cmp) return cmp.solder_ball_height - def get_vendor_libraries(self): + def get_vendor_libraries(self) -> ComponentLib: """Retrieve all capacitors and inductors libraries from ANSYS installation (used by Siwave). Returns @@ -887,7 +887,7 @@ def _get_closest_pin_from(self, pin, ref_pinlist): closest_pin = ref_pin return closest_pin - def replace_rlc_by_gap_boundaries(self, component=None): + def replace_rlc_by_gap_boundaries(self, component=None) -> bool: """Replace RLC component by RLC gap boundaries. These boundary types are compatible with 3D modeler export. Only 2 pins RLC components are supported in this command. @@ -899,7 +899,7 @@ def replace_rlc_by_gap_boundaries(self, component=None): Returns ------- bool - ``True`` when succeed, ``False`` if it failed. + ``True`` when succeeded, ``False`` if it failed. Examples -------- @@ -923,7 +923,7 @@ def replace_rlc_by_gap_boundaries(self, component=None): component.enabled = False return self._pedb.source_excitation.add_rlc_boundary(component.refdes, False) - def deactivate_rlc_component(self, component=None, create_circuit_port=False, pec_boundary=False): + def deactivate_rlc_component(self, component=None, create_circuit_port=False, pec_boundary=False) -> bool: """Deactivate RLC component with a possibility to convert it to a circuit port. Parameters @@ -1064,7 +1064,7 @@ def _create_pin_group_terminal(self, pingroup, isref=False, term_name=None, term pingroup=pingroup, term_name=term_name, term_type=term_type, isref=isref ) - def _is_top_component(self, cmp): + def _is_top_component(self, cmp) -> bool: """Test the component placement layer. Parameters @@ -1076,8 +1076,6 @@ def _is_top_component(self, cmp): ------- bool ``True`` when component placed on top layer, ``False`` on bottom layer. - - """ top_layer = self._pedb.stackup.signal[0].name if cmp.placement_layer == top_layer: @@ -1120,7 +1118,7 @@ def create( c_value=None, l_value=None, is_parallel=False, - ): + ) -> bool: """Create a component from pins. Parameters @@ -1269,7 +1267,7 @@ def create_component_from_pins( is_rlc=False, ) - def set_component_model(self, componentname, model_type="Spice", modelpath=None, modelname=None): + def set_component_model(self, componentname, model_type="Spice", modelpath=None, modelname=None) -> bool: """Assign a Spice or Touchstone model to a component. Parameters @@ -1348,7 +1346,7 @@ def set_component_model(self, componentname, model_type="Spice", modelpath=None, component.component_property.model = s_parameter_mod return True - def create_pingroup_from_pins(self, pins, group_name=None): + def create_pingroup_from_pins(self, pins, group_name=None) -> PinGroup: """Create a pin group on a component. Parameters @@ -1403,8 +1401,7 @@ def create_pingroup_from_pins(self, pins, group_name=None): pin_group.net = pins[0].net return pin_group - def delete_single_pin_rlc(self, deactivate_only=False): - # type: (bool) -> list + def delete_single_pin_rlc(self, deactivate_only=False) -> list[str]: """Delete all RLC components with a single pin. Single pin component model type will be reverted to ``"RLC"``. @@ -1443,7 +1440,7 @@ def delete_single_pin_rlc(self, deactivate_only=False): self._pedb.logger.info("Deleted {} components".format(len(deleted_comps))) return deleted_comps - def delete(self, component_name): + def delete(self, component_name) -> bool: """Delete a component. Parameters @@ -1472,7 +1469,7 @@ def delete(self, component_name): return True return False - def disable_rlc_component(self, component_name): + def disable_rlc_component(self, component_name) -> bool: """Disable a RLC component. Parameters @@ -1520,7 +1517,7 @@ def set_solder_ball( reference_size_x=0, reference_size_y=0, reference_height=0, - ): + ) -> bool: """Set cylindrical solder balls on a given component. Parameters @@ -1618,7 +1615,7 @@ def set_component_rlc( ind_value=None, cap_value=None, isparallel=False, - ): + ) -> bool: """Update values for an RLC component. Parameters @@ -1697,7 +1694,7 @@ def update_rlc_from_bom( valuefield="Func des", comptype="Prod name", refdes="Pos / Place", - ): + ) -> bool: """Update the EDC core component values (RLCs) with values coming from a BOM file. Parameters @@ -1767,7 +1764,7 @@ def import_bom( part_name_col=1, comp_type_col=2, value_col=3, - ): + ) -> bool: """Load external BOM file. Parameters @@ -1877,7 +1874,7 @@ def export_bom(self, bom_file, delimiter=","): f.writelines([delimiter.join([refdes, part_name, comp_type, value + "\n"])]) return True - def find_by_reference_designator(self, reference_designator): + def find_by_reference_designator(self, reference_designator) -> list[str]: """Find a component. Parameters @@ -1887,7 +1884,7 @@ def find_by_reference_designator(self, reference_designator): """ return self.instances[reference_designator] - def get_aedt_pin_name(self, pin): + def get_aedt_pin_name(self, pin) -> str: """Retrieve the pin name that is shown in AEDT. .. note:: @@ -1927,7 +1924,7 @@ def get_pins(self, reference_designator, net_name=None, pin_name=None): Returns ------- - + dict[str, PadStackInstance] """ comp = self.find_by_reference_designator(reference_designator) @@ -1940,7 +1937,7 @@ def get_pins(self, reference_designator, net_name=None, pin_name=None): return pins - def get_pin_position(self, pin): + def get_pin_position(self, pin) -> list[float, float]: """Retrieve the pin position in meters. Parameters @@ -1969,7 +1966,7 @@ def get_pin_position(self, pin): transformed_pt_pos = pin.component.transform.transform_point(pt_pos) return [transformed_pt_pos[0].value, transformed_pt_pos[1].value] - def get_pins_name_from_net(self, net_name, pin_list=None): + def get_pins_name_from_net(self, net_name, pin_list=None) -> list[str]: """Retrieve pins belonging to a net. Parameters @@ -2004,7 +2001,7 @@ def get_pins_name_from_net(self, net_name, pin_list=None): pin_names.append(self.get_aedt_pin_name(pin)) return pin_names - def get_nets_from_pin_list(self, pins): + def get_nets_from_pin_list(self, pins) -> list[str]: """Retrieve nets with one or more pins. Parameters @@ -2027,7 +2024,7 @@ def get_nets_from_pin_list(self, pins): """ return list(set([pin.net.name for pin in pins])) - def get_component_net_connection_info(self, refdes): + def get_component_net_connection_info(self, refdes) -> dict[str, list[str]]: """Retrieve net connection information. Parameters @@ -2059,7 +2056,7 @@ def get_component_net_connection_info(self, refdes): data["net_name"].append(net_name) return data - def get_rats(self): + def get_rats(self) -> list[dict[str, str]]: """Retrieve a list of dictionaries of the reference designator, pin names, and net names. Returns @@ -2082,7 +2079,7 @@ def get_rats(self): df_list.append(df) return df_list - def get_through_resistor_list(self, threshold=1): + def get_through_resistor_list(self, threshold=1) -> list[str]: """Retrieve through resistors. Parameters @@ -2116,7 +2113,7 @@ def get_through_resistor_list(self, threshold=1): return through_comp_list - def short_component_pins(self, component_name, pins_to_short=None, width=1e-3): + def short_component_pins(self, component_name, pins_to_short=None, width=1e-3) -> bool: """Short pins of component with a trace. Parameters @@ -2256,7 +2253,7 @@ def short_component_pins(self, component_name, pins_to_short=None, width=1e-3): i += 1 return True - def create_pin_group(self, reference_designator, pin_numbers, group_name=None): + def create_pin_group(self, reference_designator, pin_numbers, group_name=None) -> tuple[str, PinGroup]: """Create pin group on the component. Parameters @@ -2270,7 +2267,8 @@ def create_pin_group(self, reference_designator, pin_numbers, group_name=None): Returns ------- - PinGroup + tuple(str, PinGroup) + (Pingroup name, PinGroup) """ if not isinstance(pin_numbers, list): pin_numbers = [pin_numbers] @@ -2296,7 +2294,7 @@ def create_pin_group(self, reference_designator, pin_numbers, group_name=None): return group_name, PinGroup(self._pedb, pingroup) return False - def create_pin_group_on_net(self, reference_designator, net_name, group_name=None): + def create_pin_group_on_net(self, reference_designator, net_name, group_name=None) -> PinGroup: """Create pin group on component by net name. Parameters From 5b5aca27cf309d00b947145c22bc4fba57031257 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Sun, 1 Jun 2025 07:48:04 +0200 Subject: [PATCH 14/27] temp refactoring --- .../data_model/cfg_components_data.py | 6 --- .../data_model/cfg_configuration_data.py | 43 ++++++++-------- .../data_model/cfg_package_definition_data.py | 8 +-- .../data_model/cfg_pingroup_data.py | 8 --- .../data_model/cfg_ports_sources_data.py | 14 +---- .../data_model/cfg_s_parameter_models_data.py | 7 --- .../data_model/cfg_setup_data.py | 6 --- .../data_model/cfg_spice_models_data.py | 7 --- src/pyedb/grpc/database/components.py | 2 - src/pyedb/grpc/database/hierarchy/pingroup.py | 2 +- src/pyedb/grpc/database/layout/layout.py | 16 ++++++ src/pyedb/grpc/database/source_excitations.py | 51 +++++++++++++++++++ 12 files changed, 93 insertions(+), 77 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_components_data.py b/src/pyedb/configuration/data_model/cfg_components_data.py index 8051e9fb33..da5e0de1f3 100644 --- a/src/pyedb/configuration/data_model/cfg_components_data.py +++ b/src/pyedb/configuration/data_model/cfg_components_data.py @@ -46,9 +46,3 @@ class CfgComponent: rlc_model: CfgPinPairs = None solder_ball_properties: CfgSolderBallProperties = None port_properties: CfgPortProperties = None - - -@dataclass_json -@dataclass -class CfgComponents: - components: list[CfgComponent] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/cfg_configuration_data.py index 7dbb4c4e36..f1f430f6f7 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/cfg_configuration_data.py @@ -1,22 +1,22 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field import json from dataclasses_json import dataclass_json from pyedb.configuration.data_model.cfg_boundaries_data import CfgBoundaries -from pyedb.configuration.data_model.cfg_components_data import CfgComponents +from pyedb.configuration.data_model.cfg_components_data import CfgComponent from pyedb.configuration.data_model.cfg_general_data import CfgGeneral from pyedb.configuration.data_model.cfg_nets_data import CfgNets from pyedb.configuration.data_model.cfg_operations_data import CfgOperations from pyedb.configuration.data_model.cfg_package_definition_data import ( - CfgPackageDefinitions, + CfgPackageDefinition, ) from pyedb.configuration.data_model.cfg_padsatck_data import CfgPadStacks -from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroups -from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPorts, CfgSources -from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameters -from pyedb.configuration.data_model.cfg_setup_data import CfgSetups -from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModels +from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroup +from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPort, CfgSource +from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameter +from pyedb.configuration.data_model.cfg_setup_data import CfgSetup +from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModel from pyedb.configuration.data_model.cfg_stackup_data import CfgStackup @@ -29,16 +29,16 @@ def __init__(self, pedb): general: CfgGeneral = None boundaries: CfgBoundaries = None nets: CfgNets = None - components: CfgComponents = None - pin_groups: CfgPinGroups = None - sources: CfgSources = None - ports: CfgPorts = None - setups: CfgSetups = None + components: list[CfgComponent] = field(default_factory=list) + pin_groups: list[CfgPinGroup] = field(default_factory=list) + sources: list[CfgSource] = field(default_factory=list) + ports: list[CfgPort] = field(default_factory=list) + setups: list[CfgSetup] = field(default_factory=list) stackup: CfgStackup = None padstacks: CfgPadStacks = None - s_parameters: CfgSparameters = None - spice_models: CfgSpiceModels = None - package_definitions: CfgPackageDefinitions = None + s_parameters: list[CfgSparameter] = field(default_factory=list) + spice_models: list[CfgSpiceModel] = field(default_factory=list) + package_definitions: list[CfgPackageDefinition] = field(default_factory=list) operations: CfgOperations = None # TODO check for variables @@ -71,9 +71,12 @@ def export_configuration_file(self, file_path): def load_from_layout(self, filter=None): self._pedb.logger.info("Loading components") if not self._pedb.components.load_configuration_from_layout(filter=filter): - raise ("Failed importing components from layout with configuration.", Exception) - self._pedb.logger.info("Done") + raise "Failed importing components from layout with configuration." self._pedb.logger.info("Loading nets") if not self._pedb.nets.load_configuration_from_layout(filter=filter): - raise ("Failed importing nets from layout with configuration.", Exception) - self._pedb.logger.info("Done") + raise "Failed importing nets from layout with configuration." + self._pedb.logger.info("Loading pin groups") + if not self._pedb.layout.load_pingroup_configuration_from_layout(): + raise "Failed importing pin groups from layout" + self._pedb.logger.info("Loading ports") + self._pedb.source_excitation.load_ports_configuration_from_layout() diff --git a/src/pyedb/configuration/data_model/cfg_package_definition_data.py b/src/pyedb/configuration/data_model/cfg_package_definition_data.py index 83c4ad8f08..65f2d165ef 100644 --- a/src/pyedb/configuration/data_model/cfg_package_definition_data.py +++ b/src/pyedb/configuration/data_model/cfg_package_definition_data.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass from dataclasses_json import dataclass_json @@ -24,9 +24,3 @@ class CfgPackageDefinition: theta_jc: float = 0.0 height: float = 0.0 heatsink: CfgHeatSink = None - - -@dataclass_json -@dataclass -class CfgPackageDefinitions: - definitions: list[CfgPackageDefinition] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_pingroup_data.py b/src/pyedb/configuration/data_model/cfg_pingroup_data.py index cdff3eedde..485ddbc454 100644 --- a/src/pyedb/configuration/data_model/cfg_pingroup_data.py +++ b/src/pyedb/configuration/data_model/cfg_pingroup_data.py @@ -36,11 +36,3 @@ class CfgSource: magnitude: float = 0.0 positive_terminal: CfgTerminal = None negative_terminal: CfgTerminal = None - - -@dataclass_json -@dataclass -class CfgPinGroups: - pin_groups: list[CfgPinGroup] = field(default_factory=list) - ports: list[CfgPorts] = field(default_factory=list) - sources: list[CfgSource] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py index b01d5efec6..ce74e8d92c 100644 --- a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py +++ b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass from dataclasses_json import dataclass_json @@ -29,15 +29,3 @@ class CfgSource: magnitude: float = 0.0 positive_terminal: CfgTerminal = None negative_terminal: CfgTerminal = None - - -@dataclass_json -@dataclass -class CfgPorts: - ports: list[CfgPort] = field(default_factory=list) - - -@dataclass_json -@dataclass -class CfgSources: - sources: list[CfgSource] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py index 89a04b23c6..5ffa8cb57a 100644 --- a/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py +++ b/src/pyedb/configuration/data_model/cfg_s_parameter_models_data.py @@ -13,10 +13,3 @@ class CfgSparameter: components: list[str] = field(default_factory=list) reference_net: str = "" reference_net_per_component: dict[str, str] = field(default_factory=dict) - - -@dataclass_json -@dataclass -class CfgSparameters: - general: dict[str, str] = field(default_factory=dict) - s_parameters: list[CfgSparameter] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py index 094a1f3cea..9debed8b9b 100644 --- a/src/pyedb/configuration/data_model/cfg_setup_data.py +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -38,9 +38,3 @@ class CfgSetup: max_mag_delta_s: float = 0.02 dc_slider_position: int = 1 dc_ir_settings: CfgDcIrSettings = None - - -@dataclass_json -@dataclass -class CfgSetups: - setups: list[CfgSetup] = field(default_factory=list) diff --git a/src/pyedb/configuration/data_model/cfg_spice_models_data.py b/src/pyedb/configuration/data_model/cfg_spice_models_data.py index 0909cf6094..cd9c8d5791 100644 --- a/src/pyedb/configuration/data_model/cfg_spice_models_data.py +++ b/src/pyedb/configuration/data_model/cfg_spice_models_data.py @@ -18,10 +18,3 @@ class CfgSpiceModel: sub_circuit_name: str = "" apply_to_all: bool = True components: [str] = field(default_factory=list) - - -@dataclass_json -@dataclass -class CfgSpiceModels: - general: CfgSpiceModelLib = None - spice_models: list[CfgSpiceModel] = field(default_factory=list) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index de3502cace..3868a29cf1 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -47,7 +47,6 @@ ) from pyedb.configuration.data_model.cfg_components_data import ( CfgComponent, - CfgComponents, CfgPinPair, CfgPinPairs, CfgPortProperties, @@ -2283,7 +2282,6 @@ def create_pin_group(self, reference_designator, pin_numbers, group_name=None) - self._pedb.logger.error("No pin found to create pin group") return False pingroup = PinGroup.create(self._active_layout, group_name, pins) - if pingroup.is_null: # pragma: no cover raise RuntimeError(f"Failed to create pin group {group_name}.") else: diff --git a/src/pyedb/grpc/database/hierarchy/pingroup.py b/src/pyedb/grpc/database/hierarchy/pingroup.py index 560fe102fb..b16ae5c786 100644 --- a/src/pyedb/grpc/database/hierarchy/pingroup.py +++ b/src/pyedb/grpc/database/hierarchy/pingroup.py @@ -61,7 +61,7 @@ def component(self): :class:`Component ` Pin group component. """ - return Component(self._pedb, super().component) + return Component(self._pedb, next(iter(self.pins.items()))[1].component) @component.setter def component(self, value): diff --git a/src/pyedb/grpc/database/layout/layout.py b/src/pyedb/grpc/database/layout/layout.py index 64c628e73b..71c0e27439 100644 --- a/src/pyedb/grpc/database/layout/layout.py +++ b/src/pyedb/grpc/database/layout/layout.py @@ -34,6 +34,7 @@ import ansys.edb.core.primitive.primitive import ansys.edb.core.primitive.rectangle +from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroup, CfgPinGroups from pyedb.grpc.database.hierarchy.component import Component from pyedb.grpc.database.hierarchy.pingroup import PinGroup from pyedb.grpc.database.layout.voltage_regulator import VoltageRegulator @@ -239,3 +240,18 @@ def find_primitive( prims = [i for i in prims if i.layer_name in layer_name] if layer_name is not None else prims prims = [i for i in prims if i.net_name in net_name] if net_name is not None else prims return prims + + def load_pingroup_configuration_from_layout(self) -> bool: + if not self._pedb.configuration.pin_groups: + self._pedb.configuration.pin_groups = CfgPinGroups() + try: + for pin_group in self.pin_groups: + cfg_pin_group = CfgPinGroup() + cfg_pin_group.pins = list(pin_group.pins.keys()) + cfg_pin_group.name = pin_group.name + cfg_pin_group.reference_designator = pin_group.component.name + cfg_pin_group.net = pin_group.net.name + self._pedb.configuration.pin_groups.pin_groups.append(cfg_pin_group) + return True + except Exception as e: + raise e.args diff --git a/src/pyedb/grpc/database/source_excitations.py b/src/pyedb/grpc/database/source_excitations.py index dc8c4271d4..523cb44c19 100644 --- a/src/pyedb/grpc/database/source_excitations.py +++ b/src/pyedb/grpc/database/source_excitations.py @@ -2566,3 +2566,54 @@ def place_voltage_probe( ) p_terminal.reference_terminal = n_terminal return self._pedb.create_voltage_probe(p_terminal, n_terminal) + + def load_ports_configuration_from_layout(self): + from pyedb.configuration.data_model.cfg_pingroup_data import ( + CfgPorts as PingCfgPort, + ) + from pyedb.configuration.data_model.cfg_pingroup_data import ( + CfgTerminal as PinGroupCfgTerminal, + ) + from pyedb.configuration.data_model.cfg_ports_sources_data import ( + CfgPorts as CfgPorts, + ) + from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPort + from pyedb.configuration.data_model.cfg_ports_sources_data import CfgTerminal + + for port in [ + port + for _, port in self.excitations.items() + if port.terminal_type == "PinGroupTerminal" and not port.is_reference_terminal + ]: + cfg_port = PingCfgPort() + cfg_port.name = port.name + cfg_port.type = "circuit" + cfg_port.positive_terminal = PinGroupCfgTerminal() + cfg_port.negative_terminal = PinGroupCfgTerminal() + cfg_port.positive_terminal.pingroup = port._edb_object.pin_group.name + cfg_port.negative_terminal.pingroup = port.reference_terminal.pin_group.name + self._pedb.configuration.pin_groups.ports.append(cfg_port) + + for port in [ + port + for _, port in self.excitations.items() + if port.terminal_type == "PadstackInstanceTerminal" and not port.is_reference_terminal + ]: + if not self._pedb.configuration.ports: + self._pedb.configuration.ports = CfgPorts() + cfg_port = CfgPort() + cfg_port.name = port.name + cfg_port.reference_designator = port.component.name + cfg_port.positive_terminal = port._edb_object.name + if not port.reference_terminal and not port.is_circuit_port: + cfg_port.type = "coax" + elif port.reference_terminal and port.is_circuit_port: + cfg_port.type = "circuit" + cfg_port.positive_terminal = CfgTerminal() + cfg_port.positive_terminal.net = port.net.name + cfg_port.positive_terminal.pin = port._edb_object.padstack_instance.name + if cfg_port.type == "circuit": + cfg_port.negative_terminal = CfgTerminal() + cfg_port.negative_terminal.net = port.reference_terminal.net.name + cfg_port.negative_terminal.pin = port.reference_terminal.padstack_instance.name + self._pedb.configuration.ports.ports.append(cfg_port) From fc7984f8040fb47c5a8b653ec9f502d1a89ac9ad Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Sun, 1 Jun 2025 14:37:31 +0200 Subject: [PATCH 15/27] refactoring + load ports from layout --- .../data_model/cfg_components_data.py | 6 ++ .../data_model/cfg_pingroup_data.py | 20 +---- .../data_model/cfg_ports_sources_data.py | 1 + ...guration_data.py => configuration_data.py} | 54 +++++++----- src/pyedb/grpc/database/components.py | 5 +- src/pyedb/grpc/database/layout/layout.py | 7 +- src/pyedb/grpc/database/source_excitations.py | 84 ++++++++----------- src/pyedb/grpc/edb.py | 2 +- 8 files changed, 81 insertions(+), 98 deletions(-) rename src/pyedb/configuration/data_model/{cfg_configuration_data.py => configuration_data.py} (64%) diff --git a/src/pyedb/configuration/data_model/cfg_components_data.py b/src/pyedb/configuration/data_model/cfg_components_data.py index da5e0de1f3..0bd4c96731 100644 --- a/src/pyedb/configuration/data_model/cfg_components_data.py +++ b/src/pyedb/configuration/data_model/cfg_components_data.py @@ -46,3 +46,9 @@ class CfgComponent: rlc_model: CfgPinPairs = None solder_ball_properties: CfgSolderBallProperties = None port_properties: CfgPortProperties = None + + +@dataclass_json +@dataclass +class CfgComponents: + components: list[CfgComponent] diff --git a/src/pyedb/configuration/data_model/cfg_pingroup_data.py b/src/pyedb/configuration/data_model/cfg_pingroup_data.py index 485ddbc454..9ed8cd69d0 100644 --- a/src/pyedb/configuration/data_model/cfg_pingroup_data.py +++ b/src/pyedb/configuration/data_model/cfg_pingroup_data.py @@ -11,28 +11,10 @@ class CfgPinGroup: reference_designator: str = "" pins: list[str] = field(default_factory=list) net: Union[str, list[str]] = field(default_factory=list) + pingroup: str = "" @dataclass_json @dataclass class CfgTerminal: pingroup: str = "" - - -@dataclass_json -@dataclass -class CfgPorts: - name: str = "" - type: str = "" - positive_terminal: CfgTerminal = None - negative_terminal: CfgTerminal = None - - -@dataclass_json -@dataclass -class CfgSource: - name: str = "" - type: str = "" - magnitude: float = 0.0 - positive_terminal: CfgTerminal = None - negative_terminal: CfgTerminal = None diff --git a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py index ce74e8d92c..2f5d4cf16a 100644 --- a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py +++ b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py @@ -8,6 +8,7 @@ class CfgTerminal: pin: str = "" net: str = "" + pin_group: str = "" @dataclass_json diff --git a/src/pyedb/configuration/data_model/cfg_configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py similarity index 64% rename from src/pyedb/configuration/data_model/cfg_configuration_data.py rename to src/pyedb/configuration/data_model/configuration_data.py index f1f430f6f7..298a56e029 100644 --- a/src/pyedb/configuration/data_model/cfg_configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass import json from dataclasses_json import dataclass_json @@ -29,16 +29,16 @@ def __init__(self, pedb): general: CfgGeneral = None boundaries: CfgBoundaries = None nets: CfgNets = None - components: list[CfgComponent] = field(default_factory=list) - pin_groups: list[CfgPinGroup] = field(default_factory=list) - sources: list[CfgSource] = field(default_factory=list) - ports: list[CfgPort] = field(default_factory=list) - setups: list[CfgSetup] = field(default_factory=list) + components: list[CfgComponent] = None + pin_groups: list[CfgPinGroup] = None + sources: list[CfgSource] = None + ports: list[CfgPort] = None + setups: list[CfgSetup] = None stackup: CfgStackup = None padstacks: CfgPadStacks = None - s_parameters: list[CfgSparameter] = field(default_factory=list) - spice_models: list[CfgSpiceModel] = field(default_factory=list) - package_definitions: list[CfgPackageDefinition] = field(default_factory=list) + s_parameters: list[CfgSparameter] = None + spice_models: list[CfgSpiceModel] = None + package_definitions: list[CfgPackageDefinition] = None operations: CfgOperations = None # TODO check for variables @@ -48,19 +48,25 @@ def __init__(self, pedb): def load_file(self, file_path): with open(file_path, "r") as file: data = json.load(file) - self._pedb.configuration.components = CfgComponents(self._pedb).from_dict(data) self._pedb.configuration.general = CfgGeneral().from_dict(data) self._pedb.configuration.boundaries = CfgBoundaries().from_dict(data) self._pedb.configuration.nets = CfgNets.from_dict(data) - self._pedb.configuration.pin_groups = CfgPinGroups().from_dict(data) - self._pedb.configuration.sources = CfgSources.from_dict(data) - self._pedb.configuration.ports = CfgPorts().from_dict(data) - self._pedb.configuration.setups = CfgSetups().from_dict(data) + self._pedb.configuration.components = [CfgComponent().from_dict(cmp) for cmp in data.get("components", [])] + self._pedb.configuration.pin_groups = [CfgPinGroup().from_dict(pg) for pg in data.get("pin_groups", [])] + self._pedb.configuration.sources = [CfgSource().from_dict(src) for src in data.get("sources", [])] + self._pedb.configuration.ports = [CfgPort().from_dict(port) for port in data.get("ports", [])] + self._pedb.configuration.setups = [CfgSetup().from_dict(setup) for setup in data.get("setups", [])] self._pedb.configuration.stackup = CfgStackup().from_dict(data) self._pedb.configuration.padstacks = CfgPadStacks().from_dict(data) - self._pedb.configuration.s_parameters = CfgSparameters().from_dict(data) - self._pedb.configuration.spice_models = CfgSpiceModels().from_dict(data) - self._pedb.configuration.package_definitions = CfgPackageDefinitions().from_dict(data) + self._pedb.configuration.s_parameters = [ + CfgSparameter().from_dict(sp) for sp in data.get("s_parameters", []) + ] + self._pedb.configuration.spice_models = [ + CfgSpiceModel().from_dict(sp) for sp in data.get("spice_models", []) + ] + self._pedb.configuration.package_definitions = [ + CfgPackageDefinition().from_dict(pkg) for pkg in data.get("package_definitions", []) + ] self._pedb.configuration.operations = CfgOperations().from_dict(data) def export_configuration_file(self, file_path): @@ -69,14 +75,20 @@ def export_configuration_file(self, file_path): json.dump(data, file, indent=4) def load_from_layout(self, filter=None): - self._pedb.logger.info("Loading components") - if not self._pedb.components.load_configuration_from_layout(filter=filter): - raise "Failed importing components from layout with configuration." self._pedb.logger.info("Loading nets") if not self._pedb.nets.load_configuration_from_layout(filter=filter): raise "Failed importing nets from layout with configuration." + + self._pedb.logger.info("Loading components") + if not self._pedb.components.load_configuration_from_layout(filter=filter): + raise "Failed importing components from layout with configuration." + self._pedb.logger.info("Loading pin groups") if not self._pedb.layout.load_pingroup_configuration_from_layout(): raise "Failed importing pin groups from layout" + + self._pedb.logger.info("Loading sources") + self._pedb.logger.info("Loading ports") - self._pedb.source_excitation.load_ports_configuration_from_layout() + if not self._pedb.source_excitation.load_ports_configuration_from_layout(): + raise "Failed importing ports from layout" diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 3868a29cf1..df39d53a34 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -2327,12 +2327,11 @@ def load_configuration_from_layout(self, filter=None) -> bool: ------- bool. `True` when succeed. """ + self._pedb.configuration.components = [] if filter: edb_components = [cmp for cmp in list(self.instances.values()) if cmp.type in filter] else: edb_components = [cmp for cmp in list(self.instances.values())] - if not self._pedb.configuration.components: - self._pedb.configuration.components = CfgComponents() for edb_component in edb_components: cfg_component = CfgComponent() cfg_component.reference_designator = edb_component.refdes @@ -2373,7 +2372,7 @@ def load_configuration_from_layout(self, filter=None) -> bool: cfg_pin_pair.inductance = round(edb_component.ind_value, 15) cfg_pin_pair.capacitance = round(edb_component.cap_value, 15) cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) - self._pedb.configuration.components.components.append(cfg_component) + self._pedb.configuration.components.append(cfg_component) return True def apply_configuration_to_layout(self) -> bool: diff --git a/src/pyedb/grpc/database/layout/layout.py b/src/pyedb/grpc/database/layout/layout.py index 71c0e27439..c43c7505d0 100644 --- a/src/pyedb/grpc/database/layout/layout.py +++ b/src/pyedb/grpc/database/layout/layout.py @@ -34,7 +34,7 @@ import ansys.edb.core.primitive.primitive import ansys.edb.core.primitive.rectangle -from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroup, CfgPinGroups +from pyedb.configuration.data_model.cfg_pingroup_data import CfgPinGroup from pyedb.grpc.database.hierarchy.component import Component from pyedb.grpc.database.hierarchy.pingroup import PinGroup from pyedb.grpc.database.layout.voltage_regulator import VoltageRegulator @@ -242,16 +242,15 @@ def find_primitive( return prims def load_pingroup_configuration_from_layout(self) -> bool: - if not self._pedb.configuration.pin_groups: - self._pedb.configuration.pin_groups = CfgPinGroups() try: + self._pedb.configuration.pin_groups = [] for pin_group in self.pin_groups: cfg_pin_group = CfgPinGroup() cfg_pin_group.pins = list(pin_group.pins.keys()) cfg_pin_group.name = pin_group.name cfg_pin_group.reference_designator = pin_group.component.name cfg_pin_group.net = pin_group.net.name - self._pedb.configuration.pin_groups.pin_groups.append(cfg_pin_group) + self._pedb.configuration.pin_groups.append(cfg_pin_group) return True except Exception as e: raise e.args diff --git a/src/pyedb/grpc/database/source_excitations.py b/src/pyedb/grpc/database/source_excitations.py index 523cb44c19..6a6631e578 100644 --- a/src/pyedb/grpc/database/source_excitations.py +++ b/src/pyedb/grpc/database/source_excitations.py @@ -31,6 +31,7 @@ from ansys.edb.core.utility.rlc import Rlc as GrpcRlc from ansys.edb.core.utility.value import Value as GrpcValue +from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPort, CfgTerminal from pyedb.generic.general_methods import generate_unique_name from pyedb.grpc.database.components import Component from pyedb.grpc.database.layers.stackup_layer import StackupLayer @@ -2567,53 +2568,36 @@ def place_voltage_probe( p_terminal.reference_terminal = n_terminal return self._pedb.create_voltage_probe(p_terminal, n_terminal) - def load_ports_configuration_from_layout(self): - from pyedb.configuration.data_model.cfg_pingroup_data import ( - CfgPorts as PingCfgPort, - ) - from pyedb.configuration.data_model.cfg_pingroup_data import ( - CfgTerminal as PinGroupCfgTerminal, - ) - from pyedb.configuration.data_model.cfg_ports_sources_data import ( - CfgPorts as CfgPorts, - ) - from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPort - from pyedb.configuration.data_model.cfg_ports_sources_data import CfgTerminal - - for port in [ - port - for _, port in self.excitations.items() - if port.terminal_type == "PinGroupTerminal" and not port.is_reference_terminal - ]: - cfg_port = PingCfgPort() - cfg_port.name = port.name - cfg_port.type = "circuit" - cfg_port.positive_terminal = PinGroupCfgTerminal() - cfg_port.negative_terminal = PinGroupCfgTerminal() - cfg_port.positive_terminal.pingroup = port._edb_object.pin_group.name - cfg_port.negative_terminal.pingroup = port.reference_terminal.pin_group.name - self._pedb.configuration.pin_groups.ports.append(cfg_port) - - for port in [ - port - for _, port in self.excitations.items() - if port.terminal_type == "PadstackInstanceTerminal" and not port.is_reference_terminal - ]: - if not self._pedb.configuration.ports: - self._pedb.configuration.ports = CfgPorts() - cfg_port = CfgPort() - cfg_port.name = port.name - cfg_port.reference_designator = port.component.name - cfg_port.positive_terminal = port._edb_object.name - if not port.reference_terminal and not port.is_circuit_port: - cfg_port.type = "coax" - elif port.reference_terminal and port.is_circuit_port: - cfg_port.type = "circuit" - cfg_port.positive_terminal = CfgTerminal() - cfg_port.positive_terminal.net = port.net.name - cfg_port.positive_terminal.pin = port._edb_object.padstack_instance.name - if cfg_port.type == "circuit": - cfg_port.negative_terminal = CfgTerminal() - cfg_port.negative_terminal.net = port.reference_terminal.net.name - cfg_port.negative_terminal.pin = port.reference_terminal.padstack_instance.name - self._pedb.configuration.ports.ports.append(cfg_port) + def load_ports_configuration_from_layout(self) -> list[CfgPort]: + """Load configuration port from layout. + + Returns + ------- + list[CfgPort] + List of configuration ports. + """ + self._pedb.configuration.ports = [] + for _, port in self.excitations.items(): + if not port.is_reference_terminal: + cfg_port = CfgPort() + cfg_port.positive_terminal = CfgTerminal() + if port.terminal_type == "PinGroupTerminal": + cfg_port.name = port.name + cfg_port.type = "circuit" + cfg_port.positive_terminal.pin_group = port._edb_object.pin_group.name + cfg_port.negative_terminal = CfgTerminal() + cfg_port.negative_terminal.pin_group = port.reference_terminal.pin_group.name + elif port.terminal_type == "PadstackInstanceTerminal": + cfg_port.name = port.name + cfg_port.reference_designator = port.component.name + cfg_port.positive_terminal.pin = port._edb_object.name + cfg_port.positive_terminal.net = port._edb_object.net.name + if not port.reference_terminal and not port.is_circuit_port: + cfg_port.type = "coax" + elif port.reference_terminal and port.is_circuit_port: + cfg_port.type = "circuit" + cfg_port.negative_terminal = CfgTerminal() + cfg_port.negative_terminal.pin = port.reference_terminal.name + cfg_port.negative_terminal.pin = port.reference_terminal.net.name + self._pedb.configuration.ports.append(cfg_port) + return self._pedb.configuration.ports diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index ef5f13bfc1..ecfd13c261 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -47,7 +47,7 @@ from ansys.edb.core.utility.value import Value as GrpcValue import rtree -from pyedb.configuration.data_model.cfg_configuration_data import ( +from pyedb.configuration.data_model.configuration_data import ( Configuration as ConfigurationData, ) from pyedb.generic.constants import unit_converter From 7b1e6dbcf6cbe3228cfe1ca1aaff168388eb25f0 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Sun, 1 Jun 2025 21:17:59 +0200 Subject: [PATCH 16/27] refactoring + sources from layout --- .../data_model/configuration_data.py | 7 ++++ src/pyedb/grpc/database/components.py | 6 +-- src/pyedb/grpc/database/layout/layout.py | 10 ++++- src/pyedb/grpc/database/nets.py | 8 ++-- src/pyedb/grpc/database/source_excitations.py | 37 ++++++++++++++++++- src/pyedb/grpc/edb.py | 9 ++++- 6 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 298a56e029..7c6436baa9 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -88,7 +88,14 @@ def load_from_layout(self, filter=None): raise "Failed importing pin groups from layout" self._pedb.logger.info("Loading sources") + if not self._pedb.source_excitation.load_sources_configuration_from_layout(): + raise "Failed importing sources from layout" self._pedb.logger.info("Loading ports") if not self._pedb.source_excitation.load_ports_configuration_from_layout(): raise "Failed importing ports from layout" + + +# self._pedb.logger.info("Loading setups") +# if not self._pedb.load_simulation_setup_configuration_from_layoutt(): +# raise "Failed importing setups from layout" diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index df39d53a34..34d4a0dcbb 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -2313,7 +2313,7 @@ def create_pin_group_on_net(self, reference_designator, net_name, group_name=Non ] return self.create_pin_group(reference_designator, pins, group_name) - def load_configuration_from_layout(self, filter=None) -> bool: + def load_configuration_from_layout(self, filter=None) -> list[CfgComponent]: """Returns Components configuration class from layout. Parameters @@ -2325,7 +2325,7 @@ def load_configuration_from_layout(self, filter=None) -> bool: Returns ------- - bool. `True` when succeed. + list[CfgComponent. """ self._pedb.configuration.components = [] if filter: @@ -2373,7 +2373,7 @@ def load_configuration_from_layout(self, filter=None) -> bool: cfg_pin_pair.capacitance = round(edb_component.cap_value, 15) cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) self._pedb.configuration.components.append(cfg_component) - return True + return self._pedb.configuration.components def apply_configuration_to_layout(self) -> bool: for cfg_cmp in self._pedb.configuration.components.components: diff --git a/src/pyedb/grpc/database/layout/layout.py b/src/pyedb/grpc/database/layout/layout.py index c43c7505d0..a5201695ef 100644 --- a/src/pyedb/grpc/database/layout/layout.py +++ b/src/pyedb/grpc/database/layout/layout.py @@ -241,7 +241,13 @@ def find_primitive( prims = [i for i in prims if i.net_name in net_name] if net_name is not None else prims return prims - def load_pingroup_configuration_from_layout(self) -> bool: + def load_pingroup_configuration_from_layout(self) -> list[CfgPinGroup]: + """Load pin group configuration from layout. + + Returns + ------- + list[CfgPinGroup] + """ try: self._pedb.configuration.pin_groups = [] for pin_group in self.pin_groups: @@ -251,6 +257,6 @@ def load_pingroup_configuration_from_layout(self) -> bool: cfg_pin_group.reference_designator = pin_group.component.name cfg_pin_group.net = pin_group.net.name self._pedb.configuration.pin_groups.append(cfg_pin_group) - return True + return self._pedb.configuration.pin_groups except Exception as e: raise e.args diff --git a/src/pyedb/grpc/database/nets.py b/src/pyedb/grpc/database/nets.py index 36868a42ca..5389ad1641 100644 --- a/src/pyedb/grpc/database/nets.py +++ b/src/pyedb/grpc/database/nets.py @@ -633,7 +633,7 @@ def merge_nets_polygons(self, net_names_list): net_names_list = [net_names_list] return self._pedb.modeler.unite_polygons_on_layer(net_names_list=net_names_list) - def load_configuration_from_layout(self, filter=None) -> bool: + def load_configuration_from_layout(self, filter=None) -> CfgNets: """Update nets from layout inn configuration. Parameters @@ -641,11 +641,11 @@ def load_configuration_from_layout(self, filter=None) -> bool: filter : list[str], optional Provide a filter to retrieve only specific nets, for instance ["GND", "net1"] will only return The nets included in the list. When filter is `None` no filter is applied and all nets are returned. - Default valueis `None` + Default value is `None` Returns ------- - bool. `True` when succeed. + CfgNets object. """ if not self._pedb.configuration.nets: self._pedb.configuration.nets = CfgNets() @@ -656,4 +656,4 @@ def load_configuration_from_layout(self, filter=None) -> bool: power_nets = [net for net in power_nets if net in filter] self._pedb.configuration.nets.signal_nets = signal_nets self._pedb.configuration.nets.power_ground_nets = power_nets - return True + return self._pedb.configuration.nets diff --git a/src/pyedb/grpc/database/source_excitations.py b/src/pyedb/grpc/database/source_excitations.py index 6a6631e578..3fd969df2a 100644 --- a/src/pyedb/grpc/database/source_excitations.py +++ b/src/pyedb/grpc/database/source_excitations.py @@ -31,7 +31,11 @@ from ansys.edb.core.utility.rlc import Rlc as GrpcRlc from ansys.edb.core.utility.value import Value as GrpcValue -from pyedb.configuration.data_model.cfg_ports_sources_data import CfgPort, CfgTerminal +from pyedb.configuration.data_model.cfg_ports_sources_data import ( + CfgPort, + CfgSource, + CfgTerminal, +) from pyedb.generic.general_methods import generate_unique_name from pyedb.grpc.database.components import Component from pyedb.grpc.database.layers.stackup_layer import StackupLayer @@ -1077,7 +1081,7 @@ def create_current_source_on_pin(self, pos_pin, neg_pin, current_value=0, phase_ if not source_name: source_name = ( - f"VSource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}" + f"ISource_{pos_pin.component.name}_{pos_pin.net_name}_{neg_pin.component.name}_{neg_pin.net_name}" ) return self._create_terminal_on_pins( positive_pin=pos_pin, @@ -2601,3 +2605,32 @@ def load_ports_configuration_from_layout(self) -> list[CfgPort]: cfg_port.negative_terminal.pin = port.reference_terminal.net.name self._pedb.configuration.ports.append(cfg_port) return self._pedb.configuration.ports + + def load_sources_configuration_from_layout(self) -> list[CfgSource]: + """Load configuration sources from layout. + + Returns + ------- + list[CfgSource] + List of configuration sources. + """ + self._pedb.configuration.sources = [] + for _, source in self.sources.items(): + if not source.is_reference_terminal: + cfg_source = CfgSource() + cfg_source.positive_terminal = CfgTerminal() + cfg_source.type = source.boundary_type + cfg_source.reference_designator = source.component.name + cfg_source.name = source.name + cfg_source.magnitude = source.magnitude.value + cfg_source.negative_terminal = CfgTerminal() + if source.terminal_type == "PinGroupTerminal": + cfg_source.positive_terminal.pin_group = source._edb_object.pin_group.name + cfg_source.negative_terminal.pin_group = source.reference_terminal.pin_group.name + elif source.terminal_type == "PadstackInstanceTerminal": + cfg_source.positive_terminal.pin = source.params[0].name + cfg_source.positive_terminal.net = source.params[0].net.name + cfg_source.negative_terminal.pin = source.reference_terminal.name + cfg_source.negative_terminal.net = source.reference_terminal.net.name + self._pedb.configuration.sources.append(cfg_source) + return self._pedb.configuration.sources diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index ecfd13c261..c428aec73e 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -557,7 +557,11 @@ def sources(self) -> dict[str, Terminal]: ------- Dict: Dic[str, :class:`Terminal `] """ - return self.terminals + return { + k: v + for k, v in self.terminals.items() + if v.boundary_type in ["voltage_source", "current_source"] and not v.is_reference_terminal + } @property def voltage_regulator_modules(self): @@ -4135,3 +4139,6 @@ def export_gds_comp_xml(self, comps_to_export, gds_comps_unit="mm", control_path ET.indent(tree, space="\t", level=0) tree.write(control_path) return True if os.path.exists(control_path) else False + + def load_simulation_setup_configuration_from_layout(self): + pass From a92ee684b0f9585003302f519682c8bce4e3e811 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 10:25:35 +0200 Subject: [PATCH 17/27] refactoring + setup from layout --- .../data_model/cfg_setup_data.py | 1 + .../data_model/configuration_data.py | 7 ++-- src/pyedb/grpc/edb.py | 37 ++++++++++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py index 9debed8b9b..6d8b3964ba 100644 --- a/src/pyedb/configuration/data_model/cfg_setup_data.py +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -38,3 +38,4 @@ class CfgSetup: max_mag_delta_s: float = 0.02 dc_slider_position: int = 1 dc_ir_settings: CfgDcIrSettings = None + freq_sweep: CfgFrequencySweep = None diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 7c6436baa9..66200b81bb 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -95,7 +95,6 @@ def load_from_layout(self, filter=None): if not self._pedb.source_excitation.load_ports_configuration_from_layout(): raise "Failed importing ports from layout" - -# self._pedb.logger.info("Loading setups") -# if not self._pedb.load_simulation_setup_configuration_from_layoutt(): -# raise "Failed importing setups from layout" + self._pedb.logger.info("Loading setups") + if not self._pedb.load_simulation_setup_configuration_from_layout(): + raise "Failed importing setups from layout" diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index c428aec73e..fdd8a5e73f 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -4141,4 +4141,39 @@ def export_gds_comp_xml(self, comps_to_export, gds_comps_unit="mm", control_path return True if os.path.exists(control_path) else False def load_simulation_setup_configuration_from_layout(self): - pass + from pyedb.configuration.data_model.cfg_setup_data import ( + CfgDcIrSettings, + CfgFrequency, + CfgFrequencySweep, + CfgSetup, + ) + + self.configuration.setups = [] + for _, setup in self.setups.items(): + cfg_setup = CfgSetup() + setup_type_mapping = {"HFSS": "HFSS", "SI_WAVE": "siwave_syz", "SI_WAVE_DCIR": "siwave_dc"} + cfg_setup.name = setup.name + cfg_setup.type = setup_type_mapping[setup.type.name] + if setup.type.name == "HFSS": + cfg_setup.f_adapt = setup.general.single_frequency_adaptive_solution.adaptive_frequency + cfg_setup.max_num_passes = setup.general.single_frequency_adaptive_solution.max_passes + cfg_setup.max_mag_delta_s = setup.general.single_frequency_adaptive_solution.max_delta + if setup.type.name == "SI_WAVE_DCIR": + cfg_setup.dc_ir_settings = CfgDcIrSettings() + cfg_setup.dc_ir_settings.export_dc_thermal_data = setup.export_dc_thermal_data + cfg_setup.dc_slider_position = setup.dc.dc_slider_pos + if setup.type.name == "SI_WAVE": + cfg_setup.dc_slider_position = setup.dc.dc_slider_pos + cfg_setup.freq_sweep = CfgFrequencySweep() + if setup.sweep_data: + cfg_setup.freq_sweep.name = setup.sweep_data[0].name + if setup.sweep_data[0].type.name == "INTERPOLATING_SWEEP": + cfg_setup.freq_sweep.type = "Interpolation" + else: + cfg_setup.freq_sweep.type = "Discrete" + for freq_sweep in setup.sweep_data: + cfg_freq = CfgFrequency() + cfg_freq.distribution = freq_sweep.frequency_data.distribution.name + cfg_freq.start = freq_sweep.frequency_data.start_f + cfg_freq.stop = freq_sweep.frequency_data.end_f + cfg_freq.step = freq_sweep.frequency_data.step From cfc3ce1861ad1a479dc1348cedf069c0b5b8fcad Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 11:16:18 +0200 Subject: [PATCH 18/27] load setup from layout --- .../data_model/configuration_data.py | 21 ++++++++--------- src/pyedb/grpc/edb.py | 23 +++++++++++++------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 66200b81bb..13f1ce042b 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -76,25 +76,22 @@ def export_configuration_file(self, file_path): def load_from_layout(self, filter=None): self._pedb.logger.info("Loading nets") - if not self._pedb.nets.load_configuration_from_layout(filter=filter): - raise "Failed importing nets from layout with configuration." + self._pedb.nets.load_configuration_from_layout(filter=filter) self._pedb.logger.info("Loading components") - if not self._pedb.components.load_configuration_from_layout(filter=filter): - raise "Failed importing components from layout with configuration." + self._pedb.components.load_configuration_from_layout(filter=filter) self._pedb.logger.info("Loading pin groups") - if not self._pedb.layout.load_pingroup_configuration_from_layout(): - raise "Failed importing pin groups from layout" + self._pedb.layout.load_pingroup_configuration_from_layout() self._pedb.logger.info("Loading sources") - if not self._pedb.source_excitation.load_sources_configuration_from_layout(): - raise "Failed importing sources from layout" + self._pedb.source_excitation.load_sources_configuration_from_layout() self._pedb.logger.info("Loading ports") - if not self._pedb.source_excitation.load_ports_configuration_from_layout(): - raise "Failed importing ports from layout" + self._pedb.source_excitation.load_ports_configuration_from_layout() self._pedb.logger.info("Loading setups") - if not self._pedb.load_simulation_setup_configuration_from_layout(): - raise "Failed importing setups from layout" + self._pedb.load_simulation_setup_configuration_from_layout() + + self._pedb.logger.info("Loading stackup") + self._pedb.load_simulation_setup_configuration_from_layout() diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index fdd8a5e73f..64bb7b873c 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -47,6 +47,12 @@ from ansys.edb.core.utility.value import Value as GrpcValue import rtree +from pyedb.configuration.data_model.cfg_setup_data import ( + CfgDcIrSettings, + CfgFrequency, + CfgFrequencySweep, + CfgSetup, +) from pyedb.configuration.data_model.configuration_data import ( Configuration as ConfigurationData, ) @@ -4140,14 +4146,14 @@ def export_gds_comp_xml(self, comps_to_export, gds_comps_unit="mm", control_path tree.write(control_path) return True if os.path.exists(control_path) else False - def load_simulation_setup_configuration_from_layout(self): - from pyedb.configuration.data_model.cfg_setup_data import ( - CfgDcIrSettings, - CfgFrequency, - CfgFrequencySweep, - CfgSetup, - ) + def load_simulation_setup_configuration_from_layout(self) -> list[CfgSetup]: + """Import setup configuratiomn from layout. + Returns + ------- + list[CfgSetup] + List of setup configuration. + """ self.configuration.setups = [] for _, setup in self.setups.items(): cfg_setup = CfgSetup() @@ -4177,3 +4183,6 @@ def load_simulation_setup_configuration_from_layout(self): cfg_freq.start = freq_sweep.frequency_data.start_f cfg_freq.stop = freq_sweep.frequency_data.end_f cfg_freq.step = freq_sweep.frequency_data.step + cfg_setup.freq_sweep.frequencies.append(cfg_freq) + self.configuration.setups.append(cfg_setup) + return self.configuration.setups From 4b17f90100f9e5d42dc1fab57c10f1e6b7ffde10 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 15:24:16 +0200 Subject: [PATCH 19/27] load setup 2 from layout --- .../data_model/cfg_setup_data.py | 11 +-- .../data_model/configuration_data.py | 2 +- src/pyedb/grpc/database/hfss.py | 74 +++++++++++++++++-- .../simulation_setup/hfss_simulation_setup.py | 18 +++-- src/pyedb/grpc/database/stackup.py | 33 +++++++++ src/pyedb/grpc/edb.py | 6 +- 6 files changed, 122 insertions(+), 22 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py index 6d8b3964ba..8a2ed52ea6 100644 --- a/src/pyedb/configuration/data_model/cfg_setup_data.py +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from typing import Union from dataclasses_json import dataclass_json @@ -7,9 +8,9 @@ @dataclass class CfgFrequency: distribution: str = "" - start: float = 0.0 - stop: float = 0.0 - step: float = 0.0 + start: Union[str, float] = 0.0 + stop: Union[str, float] = 0.0 + step: Union[str, float] = 0.0 points: int = 0 samples: int = 0 @@ -33,9 +34,9 @@ class CfgDcIrSettings: class CfgSetup: name: str = "" type: str = "" - f_adapt: str = "" + f_adapt: Union[str, float] = "" max_num_passes: int = 20 - max_mag_delta_s: float = 0.02 + max_mag_delta_s: Union[str, float] = 0.02 dc_slider_position: int = 1 dc_ir_settings: CfgDcIrSettings = None freq_sweep: CfgFrequencySweep = None diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 13f1ce042b..36915c65d7 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -94,4 +94,4 @@ def load_from_layout(self, filter=None): self._pedb.load_simulation_setup_configuration_from_layout() self._pedb.logger.info("Loading stackup") - self._pedb.load_simulation_setup_configuration_from_layout() + self._pedb.stackup.load_configuration_from_layout() diff --git a/src/pyedb/grpc/database/hfss.py b/src/pyedb/grpc/database/hfss.py index dc0e5d6395..e410327c94 100644 --- a/src/pyedb/grpc/database/hfss.py +++ b/src/pyedb/grpc/database/hfss.py @@ -24,6 +24,7 @@ This module contains the ``EdbHfss`` class. """ import math +from typing import Union import warnings from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData @@ -1201,13 +1202,19 @@ def create_rlc_boundary_on_pins(self, positive_pin=None, negative_pin=None, rval def add_setup( self, - name=None, - distribution="linear", - start_freq=0, - stop_freq=20e9, - step_freq=1e6, - discrete_sweep=False, - ): + name: str = "", + distribution: str = "linear", + start_freq: Union[float, str] = "0Hz", + stop_freq: Union[float, str] = "20Ghz", + step_freq: Union[float, str] = "10Mhz", + discrete_sweep: bool = False, + solution_type: str = "single_frequency", + adaptive_frequency: Union[list[float, str], float, str] = 10e9, + max_num_passes: int = 20, + max_delta_s: Union[float, list[float]] = 0.02, + low_frequency: Union[float, str] = "1Ghz", + high_frequency: Union[float, str] = "100Ghz", + ) -> HfssSimulationSetup: """Add a HFSS analysis to EDB. Parameters @@ -1231,6 +1238,23 @@ def add_setup( distribution. Must be integer in that case. discrete_sweep : bool, optional Whether the sweep is discrete. The default is ``False``. + solution_type : str, optional + Give the adaptive solution type. Supported argument `single_frequency`, `multi_frequency`, `broadband`. + Default value is `single_frequency` + adaptive_frequency : Union[list[float, str], float, str], optional + Provide the adaptive solution frequency. Format must be different depending on adaptive solution type. + when `single_frequency` is set argument can be a float or string. If `multi_frequency` is set argument must + be a list[str, float]. if `broadband` is set argument argument must be a float. + max_num_passes : int, optional + Provide the maximum number of adaptive passes. + max_delta_s : Union[float, list[float]], optional + Provide the maximum delta s value for convergence. Format must be different depending on adaptive + solution type. When `single_frequency` is set argument must be float. If `multi_frequency` is set argument + must be a list[float]. if `broadband` is set argument must be a float. + low_frequency : float, optional. + Provides the start meshing frequency when broadband adaptive solution is set. + high_frequency : float, optional. + Provides the high meshing frequency when broadband adaptive solution is set. Returns ------- @@ -1255,9 +1279,43 @@ def add_setup( if name in self._pedb.setups: self._pedb.logger.error(f"HFSS setup {name} already defined.") return False - setup = GrpcHfssSimulationSetup.create(self._pedb.active_cell, name) + setup = HfssSimulationSetup(self._pedb, GrpcHfssSimulationSetup.create(self._pedb.active_cell, name)) start_freq = self._pedb.number_with_units(start_freq, "Hz") stop_freq = self._pedb.number_with_units(stop_freq, "Hz") + if solution_type.lower() == "single_frequency": + if adaptive_frequency: + if isinstance(adaptive_frequency, list): + adaptive_frequency = adaptive_frequency[0] + if max_num_passes: + if isinstance(max_num_passes, list): + max_num_passes = max_num_passes[0] + if max_delta_s: + if isinstance(max_delta_s, list): + max_delta_s = max_delta_s[0] + setup.set_solution_single_frequency( + frequency=str(adaptive_frequency), max_num_passes=max_num_passes, max_delta_s=max_delta_s + ) + if solution_type.lower() == "multi_frequency": + if not isinstance(adaptive_frequency, list): + self._pedb.logger.warning( + "Setting multi frequency adaptive setup requires to pass a list of frequency " "point" + ) + self._pedb.logger.warning("Defaulting to [1e9, 10e9]") + adaptive_frequency = [1e9, 10e9] + if not isinstance(max_delta_s, list): + self._pedb.logger.warning( + "Setting multi frequency adaptive setup requires to pass a list of max " "delta s point" + ) + self._pedb.logger.warning("Defaulting to [0.02, 0.02]") + max_delta_s = [0.02, 0.02] + setup.set_solution_multi_frequencies(frequencies=adaptive_frequency, max_delta_s=max_delta_s) + if solution_type.lower() == "broadband": + setup.set_solution_broadband( + low_frequency=low_frequency, + high_frequency=high_frequency, + max_delta_s=max_delta_s, + max_num_passes=max_num_passes, + ) if distribution.lower() == "linear": distribution = "LIN" elif distribution.lower() == "linear_count": diff --git a/src/pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py b/src/pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py index a403d8437f..3faad340bf 100644 --- a/src/pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +++ b/src/pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py @@ -21,6 +21,8 @@ # SOFTWARE. +from typing import Union + from ansys.edb.core.simulation_setup.adaptive_solutions import ( AdaptiveFrequency as GrpcAdaptiveFrequency, ) @@ -75,9 +77,9 @@ def set_solution_multi_frequencies(self, frequencies="5GHz", max_delta_s=0.02): Parameters ---------- - frequencies : str, List[str]. + frequencies : str, list[str]. Adaptive frequencies. - max_delta_s : float, List[float]. + max_delta_s : float, list[float]. Max delta S values. Returns @@ -102,14 +104,20 @@ def set_solution_multi_frequencies(self, frequencies="5GHz", max_delta_s=0.02): except: return False - def set_solution_broadband(self, low_frequency="1GHz", high_frequency="10GHz", max_delta_s=0.02, max_num_passes=10): + def set_solution_broadband( + self, + low_frequency: Union[str, float] = "1GHz", + high_frequency: Union[str, float] = "10GHz", + max_delta_s: float = 0.02, + max_num_passes: int = 10, + ): """Set solution to broadband. Parameters ---------- - low_frequency : str + low_frequency : Union[str, float] Low frequency value. - high_frequency : str + high_frequency : Union[str, float] High frequency value. max_delta_s : float Max delta S value. diff --git a/src/pyedb/grpc/database/stackup.py b/src/pyedb/grpc/database/stackup.py index 67e124b28a..9041acc4c2 100644 --- a/src/pyedb/grpc/database/stackup.py +++ b/src/pyedb/grpc/database/stackup.py @@ -52,6 +52,11 @@ from ansys.edb.core.utility.transform3d import Transform3D as GrpcTransform3D from ansys.edb.core.utility.value import Value as GrpcValue +from pyedb.configuration.data_model.cfg_stackup_data import ( + CfgLayer, + CfgMaterial, + CfgStackup, +) from pyedb.generic.general_methods import ET, generate_unique_name from pyedb.grpc.database.layers.layer import Layer from pyedb.grpc.database.layers.stackup_layer import StackupLayer @@ -2588,3 +2593,31 @@ def _convert_elevation(el): elif show: plt.show() return plt + + def load_configuration_from_layout(self) -> CfgStackup: + """Load layer stackup configuration from layout. + + Returns + ------- + CfgStackup + Configuration stackup object. + """ + self._pedb.configuration.stackup = CfgStackup() + for _, material in self._pedb.materials.materials.items(): + cfg_material = CfgMaterial( + name=material.name, + permittivity=material.permittivity, + conductivity=material.conductivity, + dielectric_loss_tangent=material.loss_tangent, + ) + self._pedb.configuration.stackup.materials.append(cfg_material) + for _, layer in self.layers.items(): + cfg_layer = CfgLayer( + name=layer.name, + type=layer.type, + material=layer.material, + thickness=layer.thickness, + fill_material=layer.dielectric_fill, + ) + self._pedb.configuration.stackup.layers.append(cfg_layer) + return self._pedb.configuration.stackup diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index 64bb7b873c..a4aab3268f 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -4161,9 +4161,9 @@ def load_simulation_setup_configuration_from_layout(self) -> list[CfgSetup]: cfg_setup.name = setup.name cfg_setup.type = setup_type_mapping[setup.type.name] if setup.type.name == "HFSS": - cfg_setup.f_adapt = setup.general.single_frequency_adaptive_solution.adaptive_frequency - cfg_setup.max_num_passes = setup.general.single_frequency_adaptive_solution.max_passes - cfg_setup.max_mag_delta_s = setup.general.single_frequency_adaptive_solution.max_delta + cfg_setup.f_adapt = setup.settings.general.single_frequency_adaptive_solution.adaptive_frequency + cfg_setup.max_num_passes = setup.settings.general.single_frequency_adaptive_solution.max_passes + cfg_setup.max_mag_delta_s = setup.settings.general.single_frequency_adaptive_solution.max_delta if setup.type.name == "SI_WAVE_DCIR": cfg_setup.dc_ir_settings = CfgDcIrSettings() cfg_setup.dc_ir_settings.export_dc_thermal_data = setup.export_dc_thermal_data From d64caace2095d973bbeb6ada9dd049eff69734bf Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 15:50:03 +0200 Subject: [PATCH 20/27] load padstacks from layout --- .../data_model/configuration_data.py | 3 ++ src/pyedb/grpc/database/padstacks.py | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 36915c65d7..5abbd452e4 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -95,3 +95,6 @@ def load_from_layout(self, filter=None): self._pedb.logger.info("Loading stackup") self._pedb.stackup.load_configuration_from_layout() + + self._pedb.logger.info("Loading padstacks") + self._pedb.padstacks.load_configuration_from_layout() diff --git a/src/pyedb/grpc/database/padstacks.py b/src/pyedb/grpc/database/padstacks.py index 9911090494..37b28c6324 100644 --- a/src/pyedb/grpc/database/padstacks.py +++ b/src/pyedb/grpc/database/padstacks.py @@ -48,6 +48,11 @@ import numpy as np import rtree +from pyedb.configuration.data_model.cfg_padsatck_data import ( + CfgDefinition, + CfgInstance, + CfgPadStacks, +) from pyedb.generic.general_methods import generate_unique_name from pyedb.grpc.database.definition.padstack_def import PadstackDef from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance @@ -1659,3 +1664,31 @@ def reduce_via_in_bounding_box(self, bounding_box, x_samples, y_samples, nets=No if item not in to_keep: all_instances[item].delete() return True + + def load_configuration_from_layout(self) -> CfgPadStacks: + """Load padstack definition configuration. + + Returns + ------- + CfgPadStacks + """ + + range_mapping = {"upper_pad_to_lower_pad": "through", "unknown_range": "unknown"} + self._pedb.configuration.padstacks = CfgPadStacks() + for _, padstack_def in self.definitions.items(): + cfg_def = CfgDefinition( + name=padstack_def.name, + hole_range=range_mapping[padstack_def.hole_range], + hole_material=padstack_def.material, + hole_diameter=padstack_def.hole_diameter, + hole_plating_thickness=padstack_def.hole_plating_thickness, + ) + self._pedb.configuration.padstacks.definitions.append(cfg_def) + for _, padstack_instance in self.instances.items(): + cfg_instance = CfgInstance( + name=padstack_instance.name, + backdrill_bottom=padstack_instance.backdrill_bottom, + backdrill_top=padstack_instance.backdrill_top, + ) + self._pedb.configuration.padstacks.instances.append(cfg_instance) + return self._pedb.configuration.padstacks From dfe2bdfa5dc22dbe76c600026c89b2890b80a400 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 21:19:27 +0200 Subject: [PATCH 21/27] load sparam spice from layout --- .../data_model/configuration_data.py | 6 +++ src/pyedb/grpc/database/definitions.py | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 5abbd452e4..d51fce51b0 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -98,3 +98,9 @@ def load_from_layout(self, filter=None): self._pedb.logger.info("Loading padstacks") self._pedb.padstacks.load_configuration_from_layout() + + self._pedb.logger.info("Loading s-parameters definitions") + self._pedb.definitions.load_s_parameters_models_from_layout() + + self._pedb.logger.info("Loading spice definitions") + self._pedb.definitions.load_spice_models_from_layout() diff --git a/src/pyedb/grpc/database/definitions.py b/src/pyedb/grpc/database/definitions.py index 536313cb22..d71295a23e 100644 --- a/src/pyedb/grpc/database/definitions.py +++ b/src/pyedb/grpc/database/definitions.py @@ -22,6 +22,8 @@ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData +from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameter +from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModel from pyedb.grpc.database.definition.component_def import ComponentDef from pyedb.grpc.database.definition.package_def import PackageDef @@ -68,3 +70,48 @@ def add_package_def(self, name, component_part_name=None, boundary_points=None): package_def.exterior_boundary = GrpcPolygonData(points=boundary_points) return PackageDef(self._pedb, package_def) return False + + def load_s_parameters_models_from_layout(self) -> list[CfgSparameter]: + """Load S-parameter component definition configuration. + + Returns + ------- + list[CfgSparameter] + """ + + self._pedb.configuration.s_parameters = [] + for s_param in [cmp for ref, cmp in self.component.items() if cmp.component_models]: + for model in s_param.component_models: + if model.component_model_type.name == "N_PORT": + cfg_model = CfgSparameter() + cfg_model.component_definition = s_param.name + cfg_model.name = model.name + cfg_model.components = list(s_param.components.keys()) + cfg_model.file_path = s_param.reference_file + cfg_model.apply_to_all = True + cfg_model.reference_net = "" + self._pedb.configuration.s_parameters.append(cfg_model) + return self._pedb.configuration.s_parameters + + def load_spice_models_from_layout(self) -> list[CfgSpiceModel]: + """Load Spice model component definition configuration. + + Returns + ------- + list[CfgSpiceModel] + """ + + self._pedb.configuration.spice_models = [] + for spice in [cmp for ref, cmp in self.component.items() if cmp.component_models]: + for model in spice.component_models: + if model.component_model_type.name == "SPICE": + cfg_spice = CfgSpiceModel + cfg_spice.component_definition = spice.name + cfg_spice.name = model.name + cfg_spice.components = list(spice.components.keys()) + cfg_spice.file_path = spice.reference_file + cfg_spice.apply_to_all = True + cfg_spice.reference_net = "" + cfg_spice.sub_circuit_name = model.name + self._pedb.configuration.spice_models.append(cfg_spice) + return self._pedb.configuration.spice_models From 7b8c965b0ed6fd4aace3debedaccd50bba81abd3 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 21:38:30 +0200 Subject: [PATCH 22/27] load init from layout --- .../data_model/cfg_boundaries_data.py | 2 +- src/pyedb/grpc/edb.py | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/pyedb/configuration/data_model/cfg_boundaries_data.py b/src/pyedb/configuration/data_model/cfg_boundaries_data.py index 5aa97ba39e..05f7150f58 100644 --- a/src/pyedb/configuration/data_model/cfg_boundaries_data.py +++ b/src/pyedb/configuration/data_model/cfg_boundaries_data.py @@ -14,7 +14,7 @@ class CfgBoundaries: dielectric_extents_type: str = "bounding_box" dielectric_base_polygon: str = "" horizontal_padding: float = 0 - honor_primitives_on_dielectric_layers: bool = type + honor_primitives_on_dielectric_layers: bool = True air_box_extents_type: str = "bounding_box" air_box_base_polygon: str = "" air_box_truncate_model_ground_layers: bool = False diff --git a/src/pyedb/grpc/edb.py b/src/pyedb/grpc/edb.py index a4aab3268f..14055314e5 100644 --- a/src/pyedb/grpc/edb.py +++ b/src/pyedb/grpc/edb.py @@ -866,8 +866,29 @@ def configuration(self) -> ConfigurationData: ------- :class:`Configuration `. """ + from pyedb.configuration.data_model.cfg_boundaries_data import CfgBoundaries + from pyedb.configuration.data_model.cfg_general_data import CfgGeneral + from pyedb.configuration.data_model.cfg_nets_data import CfgNets + from pyedb.configuration.data_model.cfg_operations_data import CfgOperations + from pyedb.configuration.data_model.cfg_padsatck_data import CfgPadStacks + from pyedb.configuration.data_model.cfg_stackup_data import CfgStackup + if not self._configuration: self._configuration = ConfigurationData(self) + self._configuration.general = CfgGeneral() + self._configuration.boundaries = CfgBoundaries() + self._configuration.nets = CfgNets() + self._configuration.components = [] + self._configuration.pin_groups = [] + self._configuration.sources = [] + self._configuration.ports = [] + self._configuration.setups = [] + self._configuration.stackup = CfgStackup() + self._configuration.padstacks = CfgPadStacks() + self._configuration.s_parameters = [] + self._configuration.spice_models = [] + self._configuration.package_definitions = [] + self._configuration.operations = CfgOperations() return self._configuration def edb_exception(self, ex_value, tb_data): From 366b8f2ed6b42fbd633e76502fc473fbf41a5a97 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Mon, 2 Jun 2025 21:57:03 +0200 Subject: [PATCH 23/27] fixing None type warning with json serialization --- .../data_model/cfg_components_data.py | 21 ++++++++++--------- .../data_model/cfg_general_data.py | 5 +++-- .../data_model/cfg_operations_data.py | 3 ++- .../data_model/cfg_ports_sources_data.py | 5 +++-- .../data_model/cfg_setup_data.py | 6 +++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/pyedb/configuration/data_model/cfg_components_data.py b/src/pyedb/configuration/data_model/cfg_components_data.py index 0bd4c96731..36c1d0eadf 100644 --- a/src/pyedb/configuration/data_model/cfg_components_data.py +++ b/src/pyedb/configuration/data_model/cfg_components_data.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from typing import Optional from dataclasses_json import dataclass_json @@ -9,9 +10,9 @@ class CfgPinPair: type: str = "series" p1: str = "1" p2: str = "2" - capacitance: str = None - inductance: str = None - resistance: str = None + capacitance: Optional[str] = None + inductance: Optional[str] = None + resistance: Optional[str] = None @dataclass_json @@ -23,9 +24,9 @@ class CfgPinPairs: @dataclass_json @dataclass class CfgSolderBallProperties: - shape: str = "None" - diameter: str = "0um" - height: str = "0um" + shape: Optional[str] = "None" + diameter: Optional[str] = "0um" + height: Optional[str] = "0um" @dataclass_json @@ -42,10 +43,10 @@ class CfgPortProperties: class CfgComponent: reference_designator: str = "" part_type: str = "" - enabled: bool = True - rlc_model: CfgPinPairs = None - solder_ball_properties: CfgSolderBallProperties = None - port_properties: CfgPortProperties = None + enabled: Optional[bool] = True + rlc_model: Optional[CfgPinPairs] = None + solder_ball_properties: Optional[CfgSolderBallProperties] = None + port_properties: Optional[CfgPortProperties] = None @dataclass_json diff --git a/src/pyedb/configuration/data_model/cfg_general_data.py b/src/pyedb/configuration/data_model/cfg_general_data.py index e85df5ded4..79e8523e2b 100644 --- a/src/pyedb/configuration/data_model/cfg_general_data.py +++ b/src/pyedb/configuration/data_model/cfg_general_data.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import Optional from dataclasses_json import dataclass_json @@ -6,5 +7,5 @@ @dataclass_json @dataclass class CfgGeneral: - spice_model_library: str = None - s_parameter_library: str = None + spice_model_library: Optional[str] = None + s_parameter_library: Optional[str] = None diff --git a/src/pyedb/configuration/data_model/cfg_operations_data.py b/src/pyedb/configuration/data_model/cfg_operations_data.py index 4120920748..f8aac826fc 100644 --- a/src/pyedb/configuration/data_model/cfg_operations_data.py +++ b/src/pyedb/configuration/data_model/cfg_operations_data.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from typing import Optional from dataclasses_json import dataclass_json @@ -34,4 +35,4 @@ class CfgCutout: @dataclass_json @dataclass class CfgOperations: - cutout: CfgCutout = None + cutout: Optional[CfgCutout] = None diff --git a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py index 2f5d4cf16a..82dc81389d 100644 --- a/src/pyedb/configuration/data_model/cfg_ports_sources_data.py +++ b/src/pyedb/configuration/data_model/cfg_ports_sources_data.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import Optional from dataclasses_json import dataclass_json @@ -17,8 +18,8 @@ class CfgPort: name: str = "" reference_designator: str = "" type: str = "" - positive_terminal: CfgTerminal = None - negative_terminal: CfgTerminal = None + positive_terminal: Optional[CfgTerminal] = None + negative_terminal: Optional[CfgTerminal] = None @dataclass_json diff --git a/src/pyedb/configuration/data_model/cfg_setup_data.py b/src/pyedb/configuration/data_model/cfg_setup_data.py index 8a2ed52ea6..cdc24d5353 100644 --- a/src/pyedb/configuration/data_model/cfg_setup_data.py +++ b/src/pyedb/configuration/data_model/cfg_setup_data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Union +from typing import Optional, Union from dataclasses_json import dataclass_json @@ -38,5 +38,5 @@ class CfgSetup: max_num_passes: int = 20 max_mag_delta_s: Union[str, float] = 0.02 dc_slider_position: int = 1 - dc_ir_settings: CfgDcIrSettings = None - freq_sweep: CfgFrequencySweep = None + dc_ir_settings: Optional[CfgDcIrSettings] = None + freq_sweep: Optional[CfgFrequencySweep] = None From cf0423800922eca81e609fc355a1cd6c7fe8d0f2 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Tue, 3 Jun 2025 16:40:33 +0200 Subject: [PATCH 24/27] Spice model --- .../data_model/configuration_data.py | 3 +- src/pyedb/grpc/database/components.py | 56 ++++++++++++++----- src/pyedb/grpc/database/definitions.py | 24 -------- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index d51fce51b0..95c5b50112 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -103,4 +103,5 @@ def load_from_layout(self, filter=None): self._pedb.definitions.load_s_parameters_models_from_layout() self._pedb.logger.info("Loading spice definitions") - self._pedb.definitions.load_spice_models_from_layout() + self._pedb.components.load_spice_models_from_layout() + # TODO check bug #556 status for Spice model. diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 34d4a0dcbb..7478aa0554 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -52,6 +52,7 @@ CfgPortProperties, CfgSolderBallProperties, ) +from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModel from pyedb.generic.general_methods import ( generate_unique_name, get_filename_without_extension, @@ -1317,7 +1318,10 @@ def set_component_model(self, componentname, model_type="Spice", modelpath=None, for pn in pin_names: spice_mod.add_terminal(terminal=str(terminal), pin=pn) terminal += 1 - component.component_property.model = spice_mod + comp_property = component.component_property + comp_property.model = spice_mod + component.component_property = comp_property + pass else: self._logger.error("Wrong number of Pins") return False @@ -2359,22 +2363,46 @@ def load_configuration_from_layout(self, filter=None) -> list[CfgComponent]: ) cfg_component.port_properties = cfg_port_properties elif edb_component.type in ["resistor", "inductor", "capacitor"]: - cfg_component.rlc_model = CfgPinPairs() - for pin_pair in edb_component.model.pin_pairs(): - cfg_pin_pair = CfgPinPair() - cfg_pin_pair.p1 = pin_pair[0] - cfg_pin_pair.p2 = pin_pair[1] - if edb_component.is_parallel_rlc: - cfg_pin_pair.type = "parallel" - else: - cfg_pin_pair.type = "series" - cfg_pin_pair.resistance = round(edb_component.res_value, 15) - cfg_pin_pair.inductance = round(edb_component.ind_value, 15) - cfg_pin_pair.capacitance = round(edb_component.cap_value, 15) - cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) + if isinstance(edb_component.model, PinPairModel): + cfg_component.rlc_model = CfgPinPairs() + for pin_pair in edb_component.model.pin_pairs(): + cfg_pin_pair = CfgPinPair() + cfg_pin_pair.p1 = pin_pair[0] + cfg_pin_pair.p2 = pin_pair[1] + if edb_component.is_parallel_rlc: + cfg_pin_pair.type = "parallel" + else: + cfg_pin_pair.type = "series" + cfg_pin_pair.resistance = round(edb_component.res_value, 15) + cfg_pin_pair.inductance = round(edb_component.ind_value, 15) + cfg_pin_pair.capacitance = round(edb_component.cap_value, 15) + cfg_component.rlc_model.pin_pairs.append(cfg_pin_pair) self._pedb.configuration.components.append(cfg_component) return self._pedb.configuration.components + def load_spice_models_from_layout(self) -> list[CfgSpiceModel]: + """Load Spice model component definition configuration. + + Returns + ------- + list[CfgSpiceModel] + """ + + self._pedb.configuration.spice_models = [] + for spice in [cmp for ref, cmp in self.instances.items() if cmp.component_models]: + for model in spice.component_models: + if model.component_model_type.name == "SPICE": + cfg_spice = CfgSpiceModel + cfg_spice.component_definition = spice.name + cfg_spice.name = model.name + cfg_spice.components = list(spice.components.keys()) + cfg_spice.file_path = spice.reference_file + cfg_spice.apply_to_all = True + cfg_spice.reference_net = "" + cfg_spice.sub_circuit_name = model.name + self._pedb.configuration.spice_models.append(cfg_spice) + return self._pedb.configuration.spice_models + def apply_configuration_to_layout(self) -> bool: for cfg_cmp in self._pedb.configuration.components.components: edb_component = self.instances[cfg_cmp.reference_designator] diff --git a/src/pyedb/grpc/database/definitions.py b/src/pyedb/grpc/database/definitions.py index d71295a23e..88de1c48e0 100644 --- a/src/pyedb/grpc/database/definitions.py +++ b/src/pyedb/grpc/database/definitions.py @@ -23,7 +23,6 @@ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameter -from pyedb.configuration.data_model.cfg_spice_models_data import CfgSpiceModel from pyedb.grpc.database.definition.component_def import ComponentDef from pyedb.grpc.database.definition.package_def import PackageDef @@ -92,26 +91,3 @@ def load_s_parameters_models_from_layout(self) -> list[CfgSparameter]: cfg_model.reference_net = "" self._pedb.configuration.s_parameters.append(cfg_model) return self._pedb.configuration.s_parameters - - def load_spice_models_from_layout(self) -> list[CfgSpiceModel]: - """Load Spice model component definition configuration. - - Returns - ------- - list[CfgSpiceModel] - """ - - self._pedb.configuration.spice_models = [] - for spice in [cmp for ref, cmp in self.component.items() if cmp.component_models]: - for model in spice.component_models: - if model.component_model_type.name == "SPICE": - cfg_spice = CfgSpiceModel - cfg_spice.component_definition = spice.name - cfg_spice.name = model.name - cfg_spice.components = list(spice.components.keys()) - cfg_spice.file_path = spice.reference_file - cfg_spice.apply_to_all = True - cfg_spice.reference_net = "" - cfg_spice.sub_circuit_name = model.name - self._pedb.configuration.spice_models.append(cfg_spice) - return self._pedb.configuration.spice_models From 51bc2bfca35c46274cf00c0a9e16d44e6121de4d Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Tue, 3 Jun 2025 21:59:00 +0200 Subject: [PATCH 25/27] Spice model 2 --- .../data_model/configuration_data.py | 3 ++ src/pyedb/grpc/database/components.py | 6 ++- src/pyedb/grpc/database/definitions.py | 18 +++++++ .../grpc/database/hierarchy/component.py | 4 +- .../database/hierarchy/s_parameter_model.py | 2 +- .../grpc/database/hierarchy/spice_model.py | 2 +- .../grpc/system/test_edb_configuration_2p0.py | 50 +++++++++++++++++++ 7 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/pyedb/configuration/data_model/configuration_data.py b/src/pyedb/configuration/data_model/configuration_data.py index 95c5b50112..3d082146d7 100644 --- a/src/pyedb/configuration/data_model/configuration_data.py +++ b/src/pyedb/configuration/data_model/configuration_data.py @@ -105,3 +105,6 @@ def load_from_layout(self, filter=None): self._pedb.logger.info("Loading spice definitions") self._pedb.components.load_spice_models_from_layout() # TODO check bug #556 status for Spice model. + + self._pedb.logger.info("Loading package definitions") + self._pedb.definitions.load_package_definition_from_layout() diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 7478aa0554..e37303f2c0 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -1321,6 +1321,8 @@ def set_component_model(self, componentname, model_type="Spice", modelpath=None, comp_property = component.component_property comp_property.model = spice_mod component.component_property = comp_property + component.model + component.model pass else: self._logger.error("Wrong number of Pins") @@ -2389,8 +2391,8 @@ def load_spice_models_from_layout(self) -> list[CfgSpiceModel]: """ self._pedb.configuration.spice_models = [] - for spice in [cmp for ref, cmp in self.instances.items() if cmp.component_models]: - for model in spice.component_models: + for spice in [cmp for ref, cmp in self.instances.items() if cmp.component_def.component_models]: + for model in spice.component_def.component_models: if model.component_model_type.name == "SPICE": cfg_spice = CfgSpiceModel cfg_spice.component_definition = spice.name diff --git a/src/pyedb/grpc/database/definitions.py b/src/pyedb/grpc/database/definitions.py index 88de1c48e0..ddfba665e5 100644 --- a/src/pyedb/grpc/database/definitions.py +++ b/src/pyedb/grpc/database/definitions.py @@ -22,6 +22,9 @@ from ansys.edb.core.geometry.polygon_data import PolygonData as GrpcPolygonData +from pyedb.configuration.data_model.cfg_package_definition_data import ( + CfgPackageDefinition, +) from pyedb.configuration.data_model.cfg_s_parameter_models_data import CfgSparameter from pyedb.grpc.database.definition.component_def import ComponentDef from pyedb.grpc.database.definition.package_def import PackageDef @@ -91,3 +94,18 @@ def load_s_parameters_models_from_layout(self) -> list[CfgSparameter]: cfg_model.reference_net = "" self._pedb.configuration.s_parameters.append(cfg_model) return self._pedb.configuration.s_parameters + + def load_package_definition_from_layout(self) -> list[CfgPackageDefinition]: + """Load package definition configuration from layout. + + Returns + ------- + list[CfgPackageDefinition] + """ + self._pedb.configuration.package_definitions = [] + for package in self.package: + cfg_package = CfgPackageDefinition() + cfg_package.name = package.name + cfg_package.height = package.height + # TODO add remaining parameters + return self._pedb.configuration.package_definitions diff --git a/src/pyedb/grpc/database/hierarchy/component.py b/src/pyedb/grpc/database/hierarchy/component.py index e81956d6e3..1863f322df 100644 --- a/src/pyedb/grpc/database/hierarchy/component.py +++ b/src/pyedb/grpc/database/hierarchy/component.py @@ -210,9 +210,9 @@ def model(self): """ if isinstance(self.component_property.model, GrpcSPICEModel): - return SpiceModel(edb_object=self.component_property.model.msg) + return SpiceModel(edb_object=self.component_property.model) elif isinstance(self.component_property.model, GrpcSParameterModel): - return SparamModel(edb_object=self.component_property.model.msg) + return SparamModel(edb_object=self.component_property.model) else: return self.component_property.model diff --git a/src/pyedb/grpc/database/hierarchy/s_parameter_model.py b/src/pyedb/grpc/database/hierarchy/s_parameter_model.py index e48009cf9f..51ea6ebcea 100644 --- a/src/pyedb/grpc/database/hierarchy/s_parameter_model.py +++ b/src/pyedb/grpc/database/hierarchy/s_parameter_model.py @@ -29,5 +29,5 @@ class SparamModel(GrpcSParameterModel): # pragma: no cover """Manage :class:`SParameterModel `""" def __init__(self, edb_object): - super().__init__(self.msg) + super().__init__(edb_object.msg) self._edb_model = edb_object diff --git a/src/pyedb/grpc/database/hierarchy/spice_model.py b/src/pyedb/grpc/database/hierarchy/spice_model.py index f671b1d77a..5dec0c03ec 100644 --- a/src/pyedb/grpc/database/hierarchy/spice_model.py +++ b/src/pyedb/grpc/database/hierarchy/spice_model.py @@ -28,7 +28,7 @@ class SpiceModel(GrpcSpiceModel): # pragma: no cover def __init__(self, edb_object=None, name=None, file_path=None, sub_circuit=None): if edb_object: - super().__init__(edb_object) + super().__init__(edb_object.msg) elif name and file_path: if not sub_circuit: sub_circuit = name diff --git a/tests/grpc/system/test_edb_configuration_2p0.py b/tests/grpc/system/test_edb_configuration_2p0.py index 8dbe6aed7a..803efc07d7 100644 --- a/tests/grpc/system/test_edb_configuration_2p0.py +++ b/tests/grpc/system/test_edb_configuration_2p0.py @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import json +import os from pathlib import Path import pytest @@ -1371,3 +1372,52 @@ def test_probes(self, edb_examples): assert edbapp.configuration.load(data, apply_file=True) assert "probe1" in edbapp.probes edbapp.close() + + def test_configfile_refactoring(self, edb_examples): + edb = edb_examples.get_si_verse() + spice_file = os.path.join(edb.edbpath, "GRM32ER72A225KA35_25C_0V.sp") + touchstone_file = os.path.join(self.example_models_path, file_folder_path)("TEDB/ANSYS-HSD_V1.aedb") + # Add setup + setup = edb.hfss.add_setup(name="test_setup") + setup.add_sweep("test_sweep") + + # Add port on pin group + power_nets = edb.components.instances["U1"].nets + power_net = next(net for net in power_nets if net in edb.nets.power and not net == "GND") + ref_pin_group = edb.components.create_pin_group_on_net(reference_designator="U1", net_name="GND") + positive_pingroup = edb.components.create_pin_group_on_net(reference_designator="U1", net_name=power_net) + edb.source_excitation.create_circuit_port_on_pin_group( + positive_pingroup[0], neg_pin_group_name=ref_pin_group[0] + ) + + # Add coax ports + pci_nets = [net for net in edb.components.IOs["X1"].nets if "PCIe" in net] + edb.source_excitation.create_coax_port_on_component(ref_des_list="X1", net_list=pci_nets) + + # Add sources + pos_pin1 = edb.components.ICs["U10"].pins["1"] + neg_pin1 = edb.components.ICs["U10"].pins["2"] + pos_pin2 = edb.components.ICs["U10"].pins["3"] + neg_pin2 = edb.components.ICs["U10"].pins["4"] + edb.source_excitation.create_current_source_on_pin(pos_pin=pos_pin1, neg_pin=neg_pin1, current_value=0.85) + edb.source_excitation.create_voltage_source_on_pin(pos_pin=pos_pin2, neg_pin=neg_pin2, voltage_value=2.5) + + # Add sparameter + for refdes, cmp in edb.components.capacitors.items(): + edb.components.set_component_model( + componentname=refdes, model_type="Touchstone", modelpath=touchstone_file, modelname="test" + ) + + # Add spice + for refdes, cmp in edb.components.inductors.items(): + edb.components.set_component_model( + componentname=refdes, model_type="Spice", modelpath=spice_file, modelname="test2" + ) + + # deactivate resistors + for refdes, comp in edb.components.resistors.items(): + comp.enabled = False + + edb.configuration.load_from_layout() + edb.configuration.export_configuration_file(r"D:\Temp\test_export.json") + edb.configuration.load_file(config_file) From e8176e84c561441bed7c994c112ec2f5d929b7c2 Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Wed, 4 Jun 2025 09:11:04 +0200 Subject: [PATCH 26/27] Spice model 2 --- src/pyedb/grpc/database/components.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index e37303f2c0..7539c6c376 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -1321,9 +1321,6 @@ def set_component_model(self, componentname, model_type="Spice", modelpath=None, comp_property = component.component_property comp_property.model = spice_mod component.component_property = comp_property - component.model - component.model - pass else: self._logger.error("Wrong number of Pins") return False From 2869a07172d1d8b9fbc672110fbdd60dee13793e Mon Sep 17 00:00:00 2001 From: svandenb-dev Date: Wed, 4 Jun 2025 10:52:39 +0200 Subject: [PATCH 27/27] Spice model 2 --- src/pyedb/grpc/database/components.py | 3 ++- src/pyedb/grpc/database/hierarchy/component.py | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pyedb/grpc/database/components.py b/src/pyedb/grpc/database/components.py index 7539c6c376..f115bebb50 100644 --- a/src/pyedb/grpc/database/components.py +++ b/src/pyedb/grpc/database/components.py @@ -1321,6 +1321,7 @@ def set_component_model(self, componentname, model_type="Spice", modelpath=None, comp_property = component.component_property comp_property.model = spice_mod component.component_property = comp_property + self.instances[componentname] = component else: self._logger.error("Wrong number of Pins") return False @@ -2388,7 +2389,7 @@ def load_spice_models_from_layout(self) -> list[CfgSpiceModel]: """ self._pedb.configuration.spice_models = [] - for spice in [cmp for ref, cmp in self.instances.items() if cmp.component_def.component_models]: + for spice in [cmp for ref, cmp in self.instances.items() if cmp.model]: for model in spice.component_def.component_models: if model.component_model_type.name == "SPICE": cfg_spice = CfgSpiceModel diff --git a/src/pyedb/grpc/database/hierarchy/component.py b/src/pyedb/grpc/database/hierarchy/component.py index 1863f322df..a538b4c54c 100644 --- a/src/pyedb/grpc/database/hierarchy/component.py +++ b/src/pyedb/grpc/database/hierarchy/component.py @@ -209,18 +209,19 @@ def model(self): """ - if isinstance(self.component_property.model, GrpcSPICEModel): + if isinstance(self.component_property.model, GrpcSPICEModel) or isinstance( + self.component_property.model, SpiceModel + ): return SpiceModel(edb_object=self.component_property.model) - elif isinstance(self.component_property.model, GrpcSParameterModel): + elif isinstance(self.component_property.model, GrpcSParameterModel) or isinstance( + self.component_property.model, SparamModel + ): return SparamModel(edb_object=self.component_property.model) else: return self.component_property.model @model.setter def model(self, value): - if not isinstance(value, PinPairModel): - self._pedb.logger.error("Invalid input. Set model failed.") - comp_prop = self.component_property comp_prop.model = value self.component_property = comp_prop