diff --git a/_unittest/test_21_Circuit.py b/_unittest/test_21_Circuit.py index 738842a7c06..220ac093ce7 100644 --- a/_unittest/test_21_Circuit.py +++ b/_unittest/test_21_Circuit.py @@ -302,3 +302,11 @@ def test_25_zoom_to_fit(self): myres = self.aedtapp.modeler.components.create_resistor("R100", 50) mycap = self.aedtapp.modeler.components.create_capacitor("C100", 1e-12) self.aedtapp.modeler.zoom_to_fit() + + def test_26_component_catalog(self): + comp_catalog = self.aedtapp.modeler.components.components_catalog + assert comp_catalog["Capacitors:Cap_"] + assert comp_catalog["capacitors:cAp_"] + assert isinstance(comp_catalog.find_components("cap"), list) + assert comp_catalog["LISN:CISPR25_LISN"].place("Lisn1") + assert not comp_catalog["Capacitors"] diff --git a/pyaedt/application/Design.py b/pyaedt/application/Design.py index 7812e70d73c..37c2bc51a68 100644 --- a/pyaedt/application/Design.py +++ b/pyaedt/application/Design.py @@ -484,7 +484,7 @@ def project_properies(self): start = time.time() if not self._project_dictionary and os.path.exists(self.project_file): self._project_dictionary = load_entire_aedt_file(self.project_file) - self._logger.info("AEDT Load time {}".format(time.time() - start)) + self._logger.info("aedt file load time {}".format(time.time() - start)) return self._project_dictionary @property diff --git a/pyaedt/generic/LoadAEDTFile.py b/pyaedt/generic/LoadAEDTFile.py index 766df000159..157e765346c 100644 --- a/pyaedt/generic/LoadAEDTFile.py +++ b/pyaedt/generic/LoadAEDTFile.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import re -from pyaedt.generic.general_methods import time_fn # -------------------------------------------------------------------- # public interface @@ -16,7 +15,7 @@ def load_entire_aedt_file(filename): Returns ------- - type + dict dictionary containing the decoded AEDT file """ @@ -342,7 +341,7 @@ def _load_entire_aedt_file(filename): """ global _count - time_fn(_read_aedt_file, filename) + _read_aedt_file(filename) main_dict = {} # load the aedt file while _count < _len_all_lines: diff --git a/pyaedt/generic/general_methods.py b/pyaedt/generic/general_methods.py index 1f8341c05b1..ae7b1fccff1 100644 --- a/pyaedt/generic/general_methods.py +++ b/pyaedt/generic/general_methods.py @@ -12,6 +12,7 @@ import inspect import itertools import re +import fnmatch try: logger = logging.getLogger("Global") @@ -566,3 +567,41 @@ def _create_pattern(k1, k2): if m: return True return False + + +@aedt_exception_handler +def filter_string(value, search_key1): + """Filter a string""" + ignore_case = True + + def _create_pattern(k1): + k1a = re.sub(r"\?", r".", k1) + k1b = re.sub(r"\*", r".*?", k1a) + pattern = r"^{}$".format(k1b) + return pattern + + if ignore_case: + compiled_re = re.compile(_create_pattern(search_key1), re.IGNORECASE) + else: + compiled_re = re.compile(_create_pattern(search_key1)) # pragma: no cover + + m = compiled_re.search(value) + if m: + return True + return False + + +@aedt_exception_handler +def recursive_glob(startpath, filepattern): + """Return a list of files matching a pattern, searching recursively from a start path. + + Keyword Arguments: + startpath -- starting path (directory) + filepattern -- fnmatch-style filename pattern + """ + return [ + os.path.join(dirpath, filename) + for dirpath, _, filenames in os.walk(startpath) + for filename in filenames + if fnmatch.fnmatch(filename, filepattern) + ] diff --git a/pyaedt/modeler/PrimitivesCircuit.py b/pyaedt/modeler/PrimitivesCircuit.py index 01fdb653aa3..64e9ce4c913 100644 --- a/pyaedt/modeler/PrimitivesCircuit.py +++ b/pyaedt/modeler/PrimitivesCircuit.py @@ -3,10 +3,18 @@ import os import math -from pyaedt.generic.general_methods import aedt_exception_handler, _retry_ntimes, generate_unique_name + +from pyaedt.generic.general_methods import ( + aedt_exception_handler, + _retry_ntimes, + generate_unique_name, + recursive_glob, + filter_string, +) from pyaedt.modeler.Object3d import CircuitComponent from pyaedt.generic.constants import AEDT_UNITS from pyaedt.generic.TouchstoneParser import _parse_ports_name +from pyaedt.generic.LoadAEDTFile import load_entire_aedt_file class CircuitComponents(object): @@ -1229,3 +1237,124 @@ def arg_with_dim(self, Value, sUnits=None): val = "{0}{1}".format(Value, sUnits) return val + + +class ComponentInfo(object): + """Class that manage Circuit Catalog info.""" + + def __init__(self, name, component_manager, props): + self._component_manager = component_manager + self.props = props + self.name = name + self.component_library = self.props.get("ComponentLibrary", None) + + def place(self, inst_name, location=[], angle=0, use_instance_id_netlist=False): + """Create a component from a library. + + Parameters + ---------- + inst_name : str, optional + Name of the instance. The default is ``None.`` + location : list of float, optional + Position on the X axis and Y axis. + angle : optional + Angle rotation in degrees. The default is ``0``. + use_instance_id_netlist : bool, optional + Whether to enable the instance ID in the net list. + The default is ``False``. + + Returns + ------- + :class:`pyaedt.modeler.Object3d.CircuitComponent` + Circuit Component Object. + + References + ---------- + + >>> oEditor.CreateComponent + """ + return self._component_manager.create_component( + inst_name=inst_name, + component_library=self.component_library, + component_name=self.name, + location=location, + angle=angle, + use_instance_id_netlist=use_instance_id_netlist, + ) + + +class ComponentCatalog(object): + """Class that indexes Circuit Sys Catalog.""" + + @aedt_exception_handler + def __getitem__(self, compname): + """Get component from name. + + Parameters + ---------- + compname : str + ID or name of the object. + + Returns + ------- + :class:`pyaedt.modeler.PrimitivesCircuit.ComponentInfo` + Circuit Component Info. + + """ + items = self.find_components("*" + compname) + if items and len(items) == 1: + return self.components[items[0]] + elif len(items) > 1: + self._component_manager._logger.warning("Multiple components found.") + return None + else: + self._component_manager._logger.warning("Component not found.") + return None + + def __init__(self, component_manager): + self._component_manager = component_manager + self._app = self._component_manager._app + self.components = {} + self._index_components() + + @aedt_exception_handler + def _index_components(self, library_path=None): + if library_path: + sys_files = recursive_glob(library_path, "*.aclb") + root = os.path.normpath(library_path).split(os.path.sep)[-1] + else: + sys_files = recursive_glob(os.path.join(self._app.syslib, self._component_manager.design_libray), "*.aclb") + root = os.path.normpath(self._app.syslib).split(os.path.sep)[-1] + for file in sys_files: + comps = load_entire_aedt_file(file) + for compname, comp_value in comps.items(): + if compname not in ["$base_index$", "$index$", "DefInfo"]: + root_name, ext = os.path.splitext(os.path.basename(file)) + full_path = os.path.normpath(file).split(os.path.sep) + id = full_path.index(root) + 1 + if self._component_manager.design_libray in full_path[id:]: + id += 1 + full_path[-1] = full_path[-1].replace(".aclb", "") + comp_value["ComponentLibrary"] = "\\".join(full_path[id:]) + self.components["{}:{}".format(root_name, compname)] = ComponentInfo( + compname, self._component_manager, comp_value + ) + + @aedt_exception_handler + def find_components(self, filter_str="*"): + """Find all components with given filter wildcards. + + Parameters + ---------- + filter_str : str + Filter String to search. + + Returns + list + List of matching component names. + """ + c = [] + for el in list(self.components.keys()): + if filter_string(el, filter_str): + c.append(el) + return c diff --git a/pyaedt/modeler/PrimitivesNexxim.py b/pyaedt/modeler/PrimitivesNexxim.py index 9bfc7458eec..18b7302bde1 100644 --- a/pyaedt/modeler/PrimitivesNexxim.py +++ b/pyaedt/modeler/PrimitivesNexxim.py @@ -2,7 +2,7 @@ import os from pyaedt.generic.general_methods import aedt_exception_handler, generate_unique_name -from pyaedt.modeler.PrimitivesCircuit import CircuitComponents +from pyaedt.modeler.PrimitivesCircuit import CircuitComponents, ComponentCatalog from pyaedt.modeler.Object3d import CircuitComponent @@ -52,12 +52,28 @@ def __getitem__(self, partname): return None + @property + def _logger(self): + return self._app.logger + def __init__(self, modeler): CircuitComponents.__init__(self, modeler) self._app = modeler._app self._modeler = modeler self._currentId = 0 - pass + self._components_catalog = None + + @property + def components_catalog(self): + """Return the syslib component catalog with all info. + + Returns + ------- + :class:`pyaedt.modeler.PrimitivesCircuit.ComponentCatalog` + """ + if not self._components_catalog: + self._components_catalog = ComponentCatalog(self) + return self._components_catalog @aedt_exception_handler def connect_components_in_series(self, components_to_connect): diff --git a/pyaedt/modeler/PrimitivesSimplorer.py b/pyaedt/modeler/PrimitivesSimplorer.py index a0c0fa83a3e..de9bf0245c3 100644 --- a/pyaedt/modeler/PrimitivesSimplorer.py +++ b/pyaedt/modeler/PrimitivesSimplorer.py @@ -1,5 +1,5 @@ from pyaedt.generic.general_methods import aedt_exception_handler -from pyaedt.modeler.PrimitivesCircuit import CircuitComponents +from pyaedt.modeler.PrimitivesCircuit import CircuitComponents, ComponentCatalog class SimplorerComponents(CircuitComponents): @@ -60,7 +60,19 @@ def __init__(self, modeler): self._app = modeler._app self._modeler = modeler self._currentId = 0 - pass + self._components_catalog = None + + @property + def components_catalog(self): + """Return the syslib component catalog with all info. + + Returns + ------- + :class:`pyaedt.modeler.PrimitivesCircuit.ComponentCatalog` + """ + if not self._components_catalog: + self._components_catalog = ComponentCatalog(self) + return self._components_catalog @aedt_exception_handler def create_resistor(self, compname=None, value=50, location=[], angle=0, use_instance_id_netlist=False):