From 71deb6ebbe6ddcdcdadd7185b3bb9239d3ed9b8e Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Thu, 15 Feb 2024 17:22:14 +0100 Subject: [PATCH 01/20] Improve documentation Add example --- README.rst | 7 +- .../{Contributing.rst => contributing.rst} | 6 +- doc/source/examples/index.rst | 7 + .../minimal_example/api_aedt_example.py | 70 ++++++++++ .../getting_started/example_toolkit.rst | 4 + doc/source/index.rst | 95 ++++++++++++-- doc/source/{Toolkit => toolkit}/api.rst | 0 doc/source/{Toolkit => toolkit}/ui.rst | 0 examples/backend/example_models/Test.aedt | 120 ------------------ .../edb_edge_ports.aedb/edb.def | Bin 20526 -> 0 bytes examples/backend/rest_api_aedt_example.py | 2 +- .../toolkit/pyaedt_toolkit/backend/api.py | 2 +- .../toolkit/pyaedt_toolkit/ui/run_frontend.py | 8 +- .../windows/create_geometry/geometry_menu.py | 4 +- pyproject.toml | 14 +- src/ansys/aedt/toolkits/common/backend/api.py | 24 ++-- .../common/backend/common_properties.json | 2 +- .../aedt/toolkits/common/backend/constants.py | 2 +- .../toolkits/common/ui/actions_generic.py | 6 +- .../common/ui/common_windows/main_window.py | 2 +- src/ansys/aedt/toolkits/common/ui/models.py | 6 +- .../utils/ui_templates/pages/ui_main_pages.py | 2 +- 22 files changed, 210 insertions(+), 173 deletions(-) rename doc/source/{Contributing.rst => contributing.rst} (90%) create mode 100644 doc/source/examples/index.rst create mode 100644 doc/source/examples/minimal_example/api_aedt_example.py create mode 100644 doc/source/getting_started/example_toolkit.rst rename doc/source/{Toolkit => toolkit}/api.rst (100%) rename doc/source/{Toolkit => toolkit}/ui.rst (100%) delete mode 100644 examples/backend/example_models/Test.aedt delete mode 100644 examples/backend/example_models/edb_edge_ports.aedb/edb.def diff --git a/README.rst b/README.rst index 700a07db..ff237a1d 100644 --- a/README.rst +++ b/README.rst @@ -23,11 +23,14 @@ PyAEDT Common Toolkit :target: https://github.com/psf/black :alt: black +The ``pyaedt-toolkits-common`` package provides common methods to create a new Ansys Electronics Desktop (AEDT) toolkit. + +A common toolkit library can bring numerous advantages, enhancing efficiency, consistency, and collaboration +in the creation and development of AEDT toolkits. -The ``pyaedt-toolkits-common`` package provides common methods to create a new toolkit using PyAEDT. This package has two main parts: backend and user interface. -The backend has some common methods to control Ansys Electronics Desktop (AEDT) session, and in addition, +The backend has some common methods to control AEDT session, and in addition, it has a REST API based on `Flask `_. The user interface has some common methods to create a desktop application based on `Pyside6 `_. diff --git a/doc/source/Contributing.rst b/doc/source/contributing.rst similarity index 90% rename from doc/source/Contributing.rst rename to doc/source/contributing.rst index 5f7a64b1..40b21177 100644 --- a/doc/source/Contributing.rst +++ b/doc/source/contributing.rst @@ -6,11 +6,11 @@ Overall guidance on contributing to a PyAnsys repository appears in in the *PyAnsys Developer's Guide*. Ensure that you are thoroughly familiar with this guide before attempting to contribute to PyAEDT or its toolkits. -The following contribution information is specific to PyAEDT toolkits. +The following contribution information is specific to this library. Clone the repository -------------------- -To clone and install the latest version of the Motor Segmentation Toolkit in +To clone and install the latest version of the PyAEDT Common Toolkit in development mode, run these commands: .. code:: @@ -22,7 +22,7 @@ development mode, run these commands: Post issues ----------- -Use the `PyAEDT Common Toolkit Issues `_ page +Use the `PyAEDT Common Toolkit Issues `_ page to create issues to report bugs and request new features. View documentation diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst new file mode 100644 index 00000000..d41a6c1c --- /dev/null +++ b/doc/source/examples/index.rst @@ -0,0 +1,7 @@ +Examples +======== +End-to-end examples show how you can use the Common toolkit API. + +.. nbgallery:: + + minimal_example/api_aedt_example.py diff --git a/doc/source/examples/minimal_example/api_aedt_example.py b/doc/source/examples/minimal_example/api_aedt_example.py new file mode 100644 index 00000000..fca2cacb --- /dev/null +++ b/doc/source/examples/minimal_example/api_aedt_example.py @@ -0,0 +1,70 @@ +# # AEDT API example +# +# This example shows how to use the common AEDT API to launch a new AEDT session in a thread, create an Icepak design +# and create a box. + +# ## Perform required imports +# +# Perform required imports. + +# + + +from ansys.aedt.toolkits.common.backend.api import AEDTCommon + +# - + +# ## Initialize toolkit +# +# Initialize the toolkit. + +toolkit = AEDTCommon() + +# ## Get toolkit properties +# +# Get the toolkit properties. + +properties_from_backend = toolkit.get_properties() + +# ## Set properties +# +# Modify properties with a dictionary. + +new_properties = {"use_grpc": True, "debug": False} +flag_set_properties, msg_set_properties = toolkit.set_properties(new_properties) + + +# ## Initialize AEDT +# +# Launch a new AEDT session in a thread. + +thread_msg = toolkit.launch_thread(toolkit.launch_aedt) + +# ## Wait for the toolkit thread to be idle +# +# Wait for the toolkit thread to be idle and ready to accept a new task. + +toolkit.wait_to_be_idle() + +# ## Connect design +# +# Connect or create a new design. + +toolkit.connect_design("Icepak") + +# ## Get toolkit properties +# +# Properties contain the project information. + +new_properties_from_backend = toolkit.get_properties() + +# ## Create a box +# +# Create a box in the design. + +box = toolkit.aedtapp.modeler.create_box([10, 10, 10], [20, 20, 20]) + +# ## Save and release AEDT +# +# Save and release AEDT. + +toolkit.release_aedt(True, True) diff --git a/doc/source/getting_started/example_toolkit.rst b/doc/source/getting_started/example_toolkit.rst new file mode 100644 index 00000000..fa81dd5c --- /dev/null +++ b/doc/source/getting_started/example_toolkit.rst @@ -0,0 +1,4 @@ +.. _toolkit_example: + +Toolkit example +=============== diff --git a/doc/source/index.rst b/doc/source/index.rst index de9be9ff..2b2e87b2 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,34 +1,46 @@ Common PyAEDT Toolkit documentation |version| ============================================= -This package provides common methods to create a new toolkit using PyAEDT. +**Useful links**: +:ref:`Installation ` | +`Source Repository `_ | +`Issues `_ + +This package provides common methods and best practices to create a new Ansys Electronics Desktop (AEDT) toolkit. .. grid:: 2 .. grid-item-card:: Getting started :fa:`person-running` :padding: 2 2 2 2 - :link: Getting_Started/index + :link: getting_started/index :link-type: doc Learn more about the library and how to install it. .. grid-item-card:: Backend API reference :fa:`book-bookmark` :padding: 2 2 2 2 - :link: Toolkit/api + :link: toolkit/api :link-type: doc Understand how to use the backend API. - .. grid-item-card:: UI API reference :fa:`book-bookmark` + .. grid-item-card:: UI API reference :fa:`scroll` :padding: 2 2 2 2 - :link: Toolkit/ui + :link: toolkit/ui :link-type: doc Understand how to use the user interface API. + .. grid-item-card:: Examples :fa:`scroll` + :padding: 2 2 2 2 + :link: examples/index + :link-type: doc + + Explore examples that show how to use the Common API. + .. grid-item-card:: Contribute :fa:`people-group` :padding: 2 2 2 2 - :link: Contributing + :link: contributing :link-type: doc Learn how to contribute to the Magnet Segmentation Toolkit codebase or documentation. @@ -36,10 +48,73 @@ This package provides common methods to create a new toolkit using PyAEDT. .. toctree:: :hidden: - Getting_Started/index - Toolkit/ui - Toolkit/api - Contributing + getting_started/index + toolkit/ui + toolkit/api + examples/index + contributing + +What is this library? +--------------------- + +This library is a common library for AEDT toolkits. It brings numerous advantages like enhancing efficiency, +consistency, and collaboration in the creation and development of AEDT toolkits. + +This library provides methods and best practices to develop AEDT toolkits. These are the main advantages of using +this library: + +Standardization +~~~~~~~~~~~~~~~ + +A common toolkit framework establishes standardized guidelines, conventions, and practices +for toolkit development. This ensures that all developers follow a consistent structure, making it easier for them to +understand and contribute to different toolkits. + +Interoperability +~~~~~~~~~~~~~~~~ + +With a common framework, different AEDT toolkits become more interoperable. +Developers can create tools that seamlessly integrate with existing toolkits, promoting a modular +and extensible ecosystem. +This interoperability is crucial for users who may need functionalities from multiple toolkits in their workflows. + +Code Reusability +~~~~~~~~~~~~~~~~ + +A common toolkit framework encourages the reuse of code components across different toolkits. +Developers can leverage existing modules, functions, and libraries, saving time and effort in creating similar +functionalities from scratch. This promotes a more efficient development process and reduces redundancy. + +Maintenability +~~~~~~~~~~~~~~ + +Standardized frameworks make it easier to maintain and update AEDT toolkits. +When changes or improvements are required, developers can follow a unified set of procedures, ensuring that updates are +applied consistently across all toolkits. +This helps in avoiding compatibility issues and streamlining the maintenance process. + +Collaboration +~~~~~~~~~~~~~ + +A common toolkit framework fosters collaboration among developers by providing a shared set of tools and practices. +It becomes easier for multiple developers to work on different aspects of the toolkit simultaneously, +as they all adhere to the same framework. +This collaborative environment can lead to faster development cycles and a more robust set of tools. + +Documentation and Training +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A standardized framework comes with consistent documentation and training resources. +This makes it easier for developers to understand the structure of the toolkit, its functionalities, and best practices. +It also facilitates the onboarding of new developers, as they can follow established guidelines and documentation. + +Quality Assurance +~~~~~~~~~~~~~~~~~ + +A common toolkit framework enables better quality assurance processes. +Standardized testing methodologies and practices can be implemented, +ensuring that each toolkit adheres to the same quality standards. +This results in more reliable and stable toolkits for end-users. Indices and tables ================== diff --git a/doc/source/Toolkit/api.rst b/doc/source/toolkit/api.rst similarity index 100% rename from doc/source/Toolkit/api.rst rename to doc/source/toolkit/api.rst diff --git a/doc/source/Toolkit/ui.rst b/doc/source/toolkit/ui.rst similarity index 100% rename from doc/source/Toolkit/ui.rst rename to doc/source/toolkit/ui.rst diff --git a/examples/backend/example_models/Test.aedt b/examples/backend/example_models/Test.aedt deleted file mode 100644 index e6748ce2..00000000 --- a/examples/backend/example_models/Test.aedt +++ /dev/null @@ -1,120 +0,0 @@ -$begin 'AnsoftProject' - Created='Thu Aug 24 17:24:15 2023' - Product='ElectronicsDesktop' - FileOwnedByWorkbench=false - $begin 'Desktop' - Version(2023, 2) - InfrastructureVersion(1, 0) - $begin 'FactoryHeader' - $begin 'geometry3deditor' - KernelVersion(2, 0) - ProjectContainsGeometry3D='0' - $end 'geometry3deditor' - $end 'FactoryHeader' - $end 'Desktop' - UsesAdvancedFeatures=false - NextUniqueID=0 - MoveBackwards=false - $begin 'HFSSEnvironment' - Version(1, 0) - $end 'HFSSEnvironment' - $begin 'PlanarEMEnvironment' - Version(1, 0) - $end 'PlanarEMEnvironment' - $begin 'Q3DEnvironment' - Version(1, 0) - $end 'Q3DEnvironment' - $begin '2DExtractorEnvironment' - Version(1, 0) - $end '2DExtractorEnvironment' - $begin 'NexximEnvironment' - Version(1, 0) - $end 'NexximEnvironment' - $begin 'NexximNetlistEnvironment' - Version(1, 0) - $end 'NexximNetlistEnvironment' - $begin 'EmitEnvironment' - Version(1, 0) - $end 'EmitEnvironment' - $begin 'Maxwell3DEnvironment' - Version(1, 0) - $end 'Maxwell3DEnvironment' - $begin 'Maxwell2DEnvironment' - Version(1, 0) - $end 'Maxwell2DEnvironment' - $begin 'RMxprtEnvironment' - Version(1, 0) - $end 'RMxprtEnvironment' - $begin 'MaxCirEnvironment' - Version(1, 0) - $end 'MaxCirEnvironment' - $begin 'SimplorerEnvironment' - Version(1, 0) - $end 'SimplorerEnvironment' - $begin 'IcepakEnvironment' - Version(1, 0) - $end 'IcepakEnvironment' - $begin 'MechanicalEnvironment' - Version(1, 0) - $end 'MechanicalEnvironment' - $begin 'SchematicEnvironment' - Version(1, 0) - $end 'SchematicEnvironment' - $begin 'geometry3deditor' - Version(1, 0) - $end 'geometry3deditor' - ReadVersion=11 - $begin 'DesignMgrEnvironment' - CompInstCounter=0 - GPortCounter=0 - NetCounter=0 - Alias('Ieee;Simplorer Elements\\Ieee', 'Std;Simplorer Elements\\Std', 'Basic_VHDLAMS;Simplorer Elements\\Basic Elements VHDLAMS\\Basic Elements VHDLAMS', 'Digital_Elements;Simplorer Elements\\Digital Elements\\Digital Elements', 'Transformations;Simplorer Elements\\Tools\\Transformations\\Transformations', 'HEV_VHDLAMS;Simplorer Elements\\HEV VHDLAMS\\HEV VHDLAMS', 'automotive_vda;Simplorer Elements\\VDALibs VHDLAMS\\automotive_vda', 'example_boardnet;Simplorer Elements\\VDALibs VHDLAMS\\example_boardnet', 'example_ecar;Simplorer Elements\\VDALibs VHDLAMS\\example_ecar', 'fundamentals_vda;Simplorer Elements\\VDALibs VHDLAMS\\fundamentals_vda', 'hybrid_emc_vda;Simplorer Elements\\VDALibs VHDLAMS\\hybrid_emc_vda', 'megma;Simplorer Elements\\VDALibs VHDLAMS\\megma', 'modelica_rotational;Simplorer Elements\\VDALibs VHDLAMS\\modelica_rotational', 'modelica_thermal;Simplorer Elements\\VDALibs VHDLAMS\\modelica_thermal', 'modelica_translational;Simplorer Elements\\VDALibs VHDLAMS\\modelica_translational', 'spice2vhd;Simplorer Elements\\VDALibs VHDLAMS\\spice2vhd', 'spice2vhd_devices;Simplorer Elements\\VDALibs VHDLAMS\\spice2vhd_devices', 'aircraft_electrical_vhdlams;Simplorer Elements\\Aircraft Electrical VHDLAMS\\Aircraft Electrical VHDLAMS', 'power_system_vhdlams;Simplorer Elements\\Power System VHDLAMS\\Power System VHDLAMS') - $end 'DesignMgrEnvironment' - $begin 'ProjectDatasets' - NextUniqueID=0 - MoveBackwards=false - DatasetType='ProjectDatasetType' - $begin 'DatasetDefinitions' - $end 'DatasetDefinitions' - $end 'ProjectDatasets' - VariableOrders[0:] - $begin 'Definitions' - $begin 'Materials' - $end 'Materials' - $begin 'SurfaceMaterials' - $end 'SurfaceMaterials' - $begin 'Scripts' - $end 'Scripts' - $begin 'DefInfo' - $end 'DefInfo' - $begin 'Compdefs' - $end 'Compdefs' - $end 'Definitions' - DesignIDServer=0 - MoveBackwards=false - $begin 'DataInstances' - DesignEditor='TopLevel' - $begin 'CompInstances' - $end 'CompInstances' - $begin 'SODInfo' - $end 'SODInfo' - $end 'DataInstances' - $begin 'WBSystemIDToDesignInstanceIDMap' - $end 'WBSystemIDToDesignInstanceIDMap' - $begin 'WBSysIDSysDetails' - $end 'WBSysIDSysDetails' - $begin 'WBConnIDConnDetails' - $end 'WBConnIDConnDetails' - $begin 'WBMaterialGuidDetails' - WBMaterialGuidMap() - $end 'WBMaterialGuidDetails' - $begin 'MinervaProjectSettingsBlk' - MinervaRemoteFilePath='' - FolderContainerString='' - $end 'MinervaProjectSettingsBlk' -$end 'AnsoftProject' -$begin 'AllReferencedFilesForProject' -$end 'AllReferencedFilesForProject' -$begin 'ProjectPreview' - IsEncrypted=false -$end 'ProjectPreview' diff --git a/examples/backend/example_models/edb_edge_ports.aedb/edb.def b/examples/backend/example_models/edb_edge_ports.aedb/edb.def deleted file mode 100644 index 02c89098e612fe188a139c1ce3afdf31648ef08d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20526 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 qipsa}Waj Tuple[bool, str]: >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.connect_aedt() >>> toolkit_api.is_aedt_connected() - (True, "Toolkit connected to process on Grpc ") + (True, "toolkit connected to process on Grpc ") >>> toolkit_api.release_aedt() """ tcs = ToolkitConnectionStatus(desktop=self.desktop) @@ -419,7 +419,7 @@ def connect_aedt(self) -> bool: is_aedt_connected = self.is_aedt_connected() if is_aedt_connected[0]: - logger.debug("Toolkit is connected to AEDT.") + logger.debug("toolkit is connected to AEDT.") return True # Connect to AEDT @@ -439,10 +439,10 @@ def connect_aedt(self) -> bool: self.desktop = pyaedt.Desktop(**desktop_args) if not self.desktop: # pragma: no cover - logger.error("Toolkit is not connected to AEDT.") + logger.error("toolkit is not connected to AEDT.") return False - logger.debug("Toolkit is connected to AEDT.") + logger.debug("toolkit is connected to AEDT.") return True def connect_design(self, app_name: Optional[str] = None): @@ -548,10 +548,10 @@ def connect_design(self, app_name: Optional[str] = None): self.properties.design_list[self.aedtapp.project_name].append(active_design) self.properties.active_project = project_name self.properties.active_design = active_design - logger.info("Toolkit is connected to AEDT design.") + logger.info("toolkit is connected to AEDT design.") return True else: # pragma: no cover - logger.error("Toolkit not connected to AEDT design.") + logger.error("toolkit not connected to AEDT design.") return False def release_aedt(self, close_projects=False, close_on_exit=False): diff --git a/src/ansys/aedt/toolkits/common/backend/common_properties.json b/src/ansys/aedt/toolkits/common/backend/common_properties.json index 17ea352e..a1a3f75a 100644 --- a/src/ansys/aedt/toolkits/common/backend/common_properties.json +++ b/src/ansys/aedt/toolkits/common/backend/common_properties.json @@ -1,6 +1,6 @@ { "aedt_version" : "2023.2", - "non_graphical" : false, + "non_graphical" : true, "active_project" : "", "active_design": "", "project_list" : [], diff --git a/src/ansys/aedt/toolkits/common/backend/constants.py b/src/ansys/aedt/toolkits/common/backend/constants.py index 46a842e5..56553f11 100644 --- a/src/ansys/aedt/toolkits/common/backend/constants.py +++ b/src/ansys/aedt/toolkits/common/backend/constants.py @@ -21,7 +21,7 @@ # SOFTWARE. """ -Toolkit common constants +toolkit common constants """ diff --git a/src/ansys/aedt/toolkits/common/ui/actions_generic.py b/src/ansys/aedt/toolkits/common/ui/actions_generic.py index 170113d1..2950aa93 100644 --- a/src/ansys/aedt/toolkits/common/ui/actions_generic.py +++ b/src/ansys/aedt/toolkits/common/ui/actions_generic.py @@ -165,7 +165,7 @@ def launch_aedt(self, selected_version, selected_process, non_graphical=False): elif res_idle: self.ui.progress.progress = 0 response = requests.get(self.url + "/health") - if response.ok and response.json() == "Toolkit is not connected to AEDT.": + if response.ok and response.json() == "toolkit is not connected to AEDT.": be_properties = self.get_properties() if be_properties["selected_process"] == 0: be_properties["aedt_version"] = selected_version @@ -206,7 +206,7 @@ def open_project(self, selected_project): elif res_idle: self.ui.progress.progress = 0 response = requests.get(self.url + "/health") - if response.ok and response.json() == "Toolkit not connected to AEDT": + if response.ok and response.json() == "toolkit not connected to AEDT": response = requests.post(self.url + "/open_project", data=selected_project) if response.status_code == 200: msg = "Project opened" @@ -331,7 +331,7 @@ def log_and_update_progress(self, msg, log_level: str = "debug", progress: Optio bar to the given progress percentage if provided. """ - # Toolkit logging + # toolkit logging log_levels = { "debug": logger.debug, "info": logger.info, diff --git a/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py b/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py index 52e5cca7..8288b2c7 100644 --- a/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py +++ b/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py @@ -36,7 +36,7 @@ def __init__(self, app): def setup_gui(self, main_window_logo=None): # Add title if not hasattr(general_settings, "main_title"): - title = "Toolkit" + title = "toolkit" else: title = general_settings.main_title self._app.ui.title_bar.set_title(title) diff --git a/src/ansys/aedt/toolkits/common/ui/models.py b/src/ansys/aedt/toolkits/common/ui/models.py index e1303953..fe56ac3f 100644 --- a/src/ansys/aedt/toolkits/common/ui/models.py +++ b/src/ansys/aedt/toolkits/common/ui/models.py @@ -38,9 +38,9 @@ class UIProperties(BaseModel): backend_port: int = 5001 debug: bool = True log_file: str = "common_frontend.log" - app_name: str = "Toolkit Wizard" - main_title: str = "Toolkit Template Wizard" - welcome_message: str = "Welcome to the Toolkit" + app_name: str = "toolkit Wizard" + main_title: str = "toolkit Template Wizard" + welcome_message: str = "Welcome to the toolkit" version: str = "v0.0.1" copyright: str = "By: x" year: int = 2024 diff --git a/src/ansys/aedt/toolkits/common/ui/utils/ui_templates/pages/ui_main_pages.py b/src/ansys/aedt/toolkits/common/ui/utils/ui_templates/pages/ui_main_pages.py index 3d57197e..0865bedd 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/ui_templates/pages/ui_main_pages.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/ui_templates/pages/ui_main_pages.py @@ -97,6 +97,6 @@ def setupUi(self, MainPages): def retranslateUi(self, MainPages): MainPages.setWindowTitle(QCoreApplication.translate("MainPages", u"Form", None)) - self.label.setText(QCoreApplication.translate("MainPages", u"Welcome to the Toolkit", None)) + self.label.setText(QCoreApplication.translate("MainPages", u"Welcome to the toolkit", None)) # retranslateUi From d404fd0d3f55978bc45e22316e3cdc4569444259 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Thu, 15 Feb 2024 17:24:46 +0100 Subject: [PATCH 02/20] Improve documentation Add example --- .pre-commit-config.yaml | 1 + doc/source/Getting_Started/Architecture.rst | 20 ++++++++++- doc/source/Getting_Started/index.rst | 38 ++++++++++++--------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fd10e3d..6d30dcca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ exclude: | src/ansys/aedt/toolkits/common/ui/utils/ui_templates/.* | tests/.* | examples/.* | + doc/getting_started/installation.rst | README.rst ) diff --git a/doc/source/Getting_Started/Architecture.rst b/doc/source/Getting_Started/Architecture.rst index 826791d5..616ff5f6 100644 --- a/doc/source/Getting_Started/Architecture.rst +++ b/doc/source/Getting_Started/Architecture.rst @@ -1,8 +1,26 @@ -.. _ref_toolkit_architecture: +.. _architecture: Architecture ============ +This repository is a common API for any new AEDT toolkit. It standardizes AEDT toolkits implementation. + +The API provides some methods to connect to an existing AEDT session, open an existing +AEDT project or initialize a new AEDT session, which should be the basic capability of any toolkit. + +The architecture is divided in two main parts: + +1. The backend, using `Flask `_. Flask creates a REST API, +which let interact different services by simply doing HTTP requests. + +2. The user interface, using `Pyside6 `_. Pyside6 has a designer tool +which allows to create user interfaces and it is translated to python directly. + +Using Flask, the toolkit becomes interface agnostic, then you can decide change it and use a WebUI for instance +as user interface. + +You can install the library like any other open source package. You can add this project as a dependency of the new toolkit. + The architecture is defined in the following picture: .. image:: ../_static/toolkit_architecture.png diff --git a/doc/source/Getting_Started/index.rst b/doc/source/Getting_Started/index.rst index d761f20b..d7bb50ea 100644 --- a/doc/source/Getting_Started/index.rst +++ b/doc/source/Getting_Started/index.rst @@ -1,35 +1,39 @@ -.. _ref_getting_started: +.. _getting_started: =============== Getting started =============== -If you want to develop a new toolkit, you need first to understand the architecture. +If you want to develop a new toolkit, you need first to learn how the :ref:`architecture` works. -This repository is a common API for any new AEDT toolkit. It standardizes AEDT toolkits implementation. +.. grid:: 2 -The API provides some methods to connect to an existing AEDT session, open an existing -AEDT project or initialize a new AEDT session, which should be the basic capability of any toolkit. + .. grid-item-card:: Architecture :fa:`person-running` + :padding: 2 2 2 2 + :link: architecture + :link-type: doc -The architecture is split in two main parts: + Learn more about the toolkit architecture. -1. The backend, using `Flask `_. Flask creates a REST API, -which let interact different services by simply doing HTTP requests. + .. grid-item-card:: Installation :fa:`book-bookmark` + :padding: 2 2 2 2 + :link: installation + :link-type: doc -2. The frontend, using `Pyside6 `_. Pyside6 has a designer tool -which allows to create user interfaces and it is translated to python directly. + Learn how to install the library. -Using Flask, the toolkit becomes interface agnostic, then you can decide change it and use a WebUI for instance -as user interface. + .. grid-item-card:: Example toolkit :fa:`scroll` + :padding: 2 2 2 2 + :link: example_toolkit + :link-type: doc -You can install the library like any other open source package. You can add this project as a dependency of the new toolkit. - -If you need more information, go to :ref:`ref_toolkit_architecture`. + Example toolkit using the common methods. .. toctree:: :hidden: :maxdepth: 2 - Architecture - Installation + architecture + installation + example_toolkit From 4bb8ec5f13562d49d01fe3805d632d0563bc3bed Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 11:54:24 +0100 Subject: [PATCH 03/20] Add example --- doc/source/examples/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index d41a6c1c..fc772b46 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -4,4 +4,4 @@ End-to-end examples show how you can use the Common toolkit API. .. nbgallery:: - minimal_example/api_aedt_example.py + minimal_example/api_aedt.py From 4662a8386f81e3c6c7bdd23f2bd61070311501ef Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 11:54:31 +0100 Subject: [PATCH 04/20] Add example --- doc/source/_static/thumbnails/coaxial.png | Bin 0 -> 83479 bytes doc/source/conf.py | 4 +++ .../{api_aedt_example.py => api_aedt.py} | 25 ++++++++++-------- .../common/backend/common_properties.json | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 doc/source/_static/thumbnails/coaxial.png rename doc/source/examples/minimal_example/{api_aedt_example.py => api_aedt.py} (67%) diff --git a/doc/source/_static/thumbnails/coaxial.png b/doc/source/_static/thumbnails/coaxial.png new file mode 100644 index 0000000000000000000000000000000000000000..bff5001b959b8bf7a49f824a784a177f0f423b40 GIT binary patch literal 83479 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 p_Om& z*;M2KMd_~JcK3v5JQmw;9ADyx(Scj*E;r!#7tRra6Gf>KjlHeW7{IB=0)!!lJSPT7 ziSlZ!%n2}q?{=yXoG7V5Zff(bv1Lx9@W$VpO_oK?+oder=MfQF`T3*p)^_?26R(?y zGl#PLf4+Skn}93ezTWPV1NZhMs*;0=@ivN@xD&+>Q4kv$rS9?~10^Sa-Jbc+YSRKV z)}6Z_VS;h|^~V!tYG8ZlY~|xr=ex!TF+3`895K@Y_hvuBqj}wx20&*2wY8-M($lj< zv!tbMO6@ZV>u{2%jY~4pQp_aNd%7rVJgr9136!*)KIlPs?l-G=ts(O1QfX#2i|>h8o>Wux;1UkK z){%m1+IZ1P2LKJoojyIS1&XDZpIvIz`gRPmT`H1~ zw-q7X!A#(XscmoL$v8QBca}D>=wWUS*9~gN;E~Z6W5zlQ@)E@DmZ$QT+-xWnRe3W@ z>&HqGE(YI^urOr?h4}DfN~(W(_X={hS{2cwNU-U~Al`hKOFcPd>epOYX2-|#TOB@V zJKK19`aONnsmt=~dj|&{Y%D`VLm!3L0`BhiBD>4)$-M_<8C4{DU!T7BPVevkPGOaD zqLPjxt}c(+JRFRmKS;g9G`NS?mX5~JFASh4?*o*K4+Un*gMBkOvgJitxLtP z0G9`4h#Vn^5xjGZS}DD_duGVPhkJPvxw2wYqsV(z@;=7Qnj zW3lzf2^ajZluK@aYX`J=y~*~z|4y$&d@3mUiZ3f+&~^_B%Wmsai^ zu`mv7d;%Ia~R=V5IxdN;)5G_eU0)B7TS_%O)-f=#gUZTI7Y2oc*`Jf@fS@9C!w zUSE-Ie->?Dh6VR2&vr>dq=iKqMH5k8o*F2hSIMt!!2K1{aizAazj!*BD+6esb*ZwU zSlp;pRh%AX^FNoqWDrqMez$CqOTPCtp$!=NWgcmQ*=<%d$D@!aU_0O`#yxkl@(x+X(90T^nr=ZszIV9{AfjbhTJ3} zT#q62q3r<-1U#_XQZ;mfxo;L-6p^5?~DvHZa0gy zh5?2Hz`s^1UXY!QrSBp}Phd8AE{j`@pfE7%55aP%k(aAK#EoM9z|~VF1lnIq$Kg+_ z*en|Ve!LI(+O-t8e0u1b)x6Zwm(UV-!90e9fz69yo(P5I+zfSkq{77cO(A+Die-ClH+q&um?uJ1TK z%Y3Wht`tu;2GnsFA4=2r(vssCy@bXaQQ8&#YdLSB62J84M-vZ{8pTiSweLbR82`B6 zmga=vRqGRetgcVC0i?lD3+!AP*9K+8Pm)*LfNnOU`msSjY{#FdrY# zsB~XFZ5a5r?7RCrgBXXw5}PpGt3+;=wr&8S;liLNDh*@zXBKO88tdR^wtFr2_W_9Q z7@U3plKcCY(uH5YR7t*&Q)ZJWsMr%?HB~S~@wO6S%`6^d{%UPqx;cM(bzTuCJd6)HKZ=#dVEQkV*_1PIVamY*INl2kW; zI!MM>YTUSMvpoh6cJ@U!etT=v{w}e{e8e3TF2$A@6~QHp;ts)Az%q_3mUmeaq@ z*AdNC+SQ}a9Rrj#yHQa$BpzTZEruJ_z^pYhQnWO#$}}$3-t^kkE?ZOvAvz7liyE=n zbJ9nGRWIc`^VDO%NOCFCCIl%ZvCLi}j z(Hqkw7;{RJd`O>tX-);dvSZPPX=s?JPC_!Q;#-aEYh9{3;swN+&*tVYmWnpiUOGcx z4|em6&c>DL4K}zX$Ef=HUn`Vi(-#Z?ZvyAxoY$$tch!Kp1(-rF=uF2S$kuo7E2~$j z3GHzoYJvOH=O^293H3i};hfx3XYnFb+hlGIn5dFJx!vA5d|7B$7qlx-g`lp*b@wBj zeRN!Olo_2G$z0Bsc%lWAWQVZ}Rrg(oO*qS^XH#>3@=3*(_;^;oN5J4V8if(5TZzi;yy#^uE}d@23)v6s-XPp?Xt!NS;*Q z8uT^jjaz>rOxW-hiTp2oy%6qO)big9&~DguGQg<~G^u;=l3roq{$ltN_c1 zHwJo;%FVm)m6a)1`OKX86e5t)vUNbA=4^%K2Q^rMh(4irQpOT^;Rt47t=kCZ?U5(! zgW8VMi8t590UFS~Sy@lHypA?$UR!CAZ7)W#4iC5Yh9V-wGk!wR=A3~zs__Em!{LU* zvX9+yG-0~^H8kiw53P)Mo(1*WPM1x{m3ih?65j39&G?beYN74_fzj8u+= zN6f1NiQ?g(DF8D90|7cN1*{^1*Ay`VNk33l#zWP4lj^Cp#)F3H zJD|BM25uh(Z+)dZvhWVDhL|DVNlvc8JhKtOqB#5qxr|_AvycsHcgYc60!(vgP8EzD zcx6;(hRy{wO>`YC)(vOgpHowbZ?CvoXRXm7c)?MFbb0M-W7y5voK!d)rEC9<0G1#Rl^ z$RFBQ>3}}L4h$!s6(U84_##K0UjR`wD0CeJXn@Fzi8YQdlaSw6xExL?Zf42E@0z+_ zY*kcl-#06-5({rrLLYx#%yEh(_^vaIB1t` zFf?t-H62m~tLV%eMs%tA>Ol)BwPvuQf{1|iT7n4;c+-FIIzMS8ALW%b(?nj+5L|xM zm^JP@MS3YwQRPk7R`J1!AT{Zl4eH|A7_ijQAA){7v?wsQjkPtNq}@Inv=D}kRJSSh z)_lr5a(o;Ruv#o)&AJpOX=Nxz7C@xYJyRoML6wv<-)$P#yU6_&=Rx z@I^6>k#V-+VqQ$gX?><(dR5|n=uQ+J`L}`yZvw8!IGi}I1WZ(VuX|ucvGYb#cS;8W zg>nSt%L`myeB)0IAIEBCcJ1o&H#GDo)kkTsc~{=2$Qx8ot-Aq4++9nlLJZ!^wc;G?8|Rx=Yuh$=P6K+wz-RS1=m$s%A*hkK1ZCe8M`A}Q22wb~Mu=j%;0Cp4d#edCne?GDq2EC@<>;4MbV=W)U3z+%wFpTwV$xf$ zUwLrdSbQ+jG#1ofMEBTrQW{8T3>yxmE9-Gi_D)wWp%r~AE4RPoK2@18amL|FVwOZ$ zw_kXx+6=UBbxC0arp|q0hAk@O}-C^!c z0rt0x5L{ciXMP2NoK53$sX?h3_Gx@&OIvROSHu4T@1=Y7rbi3k3we7u-z4MkBDf$> z69OaMSH_8UCwjg}QCGM)vR}RWTO*l_7A@Cn95*zceKWrKf=D)uYLWY%obN#LMf~3kP&PG9_lO1yD|lm>_T-SIeJ7uoti)<7%WO z3;0xUywwK3rHX0Xt{lXrp8AcOj?Q@cMsR(wT34U<5P&-OOFpQkXje?5w^b?(nCl2cRpAz|bCl$1@Hz8T<%!Cd};A^#P`Hu!8( zW7*c`CL_h~{#rmpL_h(FkjvuXX7q+08E64iL}Y#~9Gn|k`VLblq;D8KYE(>T0B&sh zr@~QjEZW9}Ky&Vy!+1t>Djm@1l2l>s?pZ9?_vfui)6>5R4bc*DMTgYn1C#LPB7wfa+amVvV^|z&Trh0IY%z88C2gJhLtLK?nci{r2%_ z#?7lrL_qdFT~*j7RQWdye76CAru@$=sNPWVV)Q)nOQt~VKc;vVetr+%boC;<_VxYs z;Djz|PoHgv$``SxLfCvhVk8CUHT$zXWm)S_j$9-#j3C0BD$-1S4UxFd{Q`kcaFf6T zKWENGf%{36-w{(D%-4+$so9Fyqzp`ScshI0gVBiylnteC1Md0}iA$K&Ky|n}1je!x zQU2t!acU(uktnWQ98s#1F6oDkWZ!@kr0z=Q4%NyBCb}9z_&*4wf3j0k5Atn zl(qC2fp0_LJf>^`F%Dz$V%u_jR7CiWpuG=H96kOHb7aKD+c@O2DV(ko4bHL7fjfqfDAb<+oaohFjQF}&~5swRffy2L;>E> zfF8o*C;p)D;-T|L;EwBitZ;iD+2=gyYgQsU))}uC*SqR(V5!7pB9CkM!-p71>&F4> zWF+oU&j}+>7VF`RP&M{)muR-bH78@eux`K6iIt*$ZhagC9WaLB)b{A9L57aY0$o)4`q(!m2xXarYXWTTt>(&EV_X-4Rn?%tXlj07Mw#^IPY`@agT4*>Q zfj!-yl*cXT-Lv5$17+%#j#vPRt~R0!ybNbXhFfl~HmR_43N!*^t;c)}D zkoL1!)6lCs;soE{FGoAh9HzT#%r_IIS+9`+iEO};>_&7Q*PCRy>@aEr1q-{+v0c8T zetys~VWfGd3pPF%HKIZ;o}u^vxQ?U({@2}zTOUv=YSy*f^Zm`ieu~dO;7I-VSP1H~ z;{%@5=YB*+jU|XJgp41$E;x16NdRZ~sTO+z=qt~6FZ}1o8?ny~7J~Z!ey!HEb8vv@ ze1Cmv>shc>Z9PuoWd3(=YU;3`pgk_&f|h!-Y59R82tfJ5C@3f-?&d}5dbT`~X- zzq_NR%BCjY-dGBd_`|NM)v|sF_J&5OY4ff!;4*C-AQh;0Tn%`~cwnctq@K!TuIn1qTd@K*eO#nxFqcr2$4#pt@8c&qLb(|HtVa z1|Ut*H3Z7*cAimD1e4RX9x{IWzIu;;W{H>g>IUVswMpDfM?I%U?di4mwHSD>;H&-d z0|cD2VWtZp(v4gFZZuTIJWhLvi~*5A04r5o3l+K182CP<;TmqV?b*1>C)-x5IdHrV6*ES z7aM^;&oJs(k)xH5z%4V&?!T4oQ`VmSAYMciNEfsOptpUS;v^pHO-xo004a`Bu1gcxM)=y>_wYWNEhAR+v~{|>N2 zz_=`t*!4;%+q?cm`sU!ey%UcaLzESB%DdUK&B3;_|7wHn%|0LqK1Eu^^=ad}} z>^9PjVrKEd!#Az)=-T6)6F0-BB2v&ClR+t{{=)=JV{Qo9y}dc{p;B z_Xhw0!tZ(&CbkYpDsHXCa9R#0s6G!`XGn3xUdLlVo zLGbExO&scg6BmG%I(bvkbkw-~aOBxMY$b808}L7drS(!@v#V_Rta{%`)CyXOAamp* zxbNW%IQ?ALT-fk@-DDp7F>;Fd`1-bd1hgb4=C6TMH~iN>3UCKfo{jrV7Rz|pGL#I+y0T>joQ#juJk^uTQTyw_$ZO;E<0WNDR z#h&iZp8#6P63}qK(IVK!^8mj9zX9#PIqqYJ$Kt5}k=ye@BufCtVrpgvBMQ{qljP+! zVDda>v;se5c`kXg?Xw!+pzl6cxx~A9o#GfEwz!d7jpxb2uqp8H;JjEbPOmpP18)-I z{5BXRZu)}}P#%G71NQZoUkXSyKKFH7%>4X(fZ0G8gaYw0>4_#})v4IP54^~y^k2iz zep_pstY$k^ss;4hC!kfhU%ssIyE%Ji7%a67Fzirh19?+LQf1>krzP`m{RA(C_&Xf$Hm4`jd|8HVvJ z9UZzKl|8v=$?zM78mugq5%KgA;0`6)NxbKcSfVy-8f2mIPuS8cQgx;(8~$o^Y^aEx zlXC1WT|N(g)9oXNJXVvI?L5dQr#f5|nb~6MR&ONJ2JzsAQq}(eU1oSA0_cYtgnapA zfHclvlcNC^HwlFUWe%T{H3DJmS-7}Ax9{#WDalmDKQlLLJq1bNwW3*X+I znI_x6KYqNiDK-Sv$MT@n1LH*c;Ix@r@V#k1BU>$MjFayZ9$?q7465S=HMZ#PDhD!R z5+=a3#1s!L@jh!|qwVud8a?TP=G9=+@bH)8S%!UBz(TS#*1!$eO8V3XkuFfQqy%FCcakBUOG66myl#&! z=e^W~1Y@j>%g;Icjm&0jv<~*;d1>w!aDoxM_~)_SQb+Ytcvx2;ucMx7 zBsa@jtV1NkXj!K(Kbm~6LStSSs&Qk!bH zrUdwVl4q8{=v!@Hs^@x%ekX7xCJDC0(Rra$6T&G*X4=6v?8wv|>C`27k^WVh-Zvd*3ZH^Owa%*G#A|aYr>+Ep%P;mqzdD?TT=$ zCsiSq=KsUgdq>0dMt!_`Fh)1Z=t7i1qDS;WL?ng~M6V&yyXa*`i<;5vh)&dG)F=}~ zjcCz(C(#pvaF5^nzW3gLtYt0coH^%t_Or`(e|BkyKt1wH1oBz=yqoEq&tkQoH2NSz z?m9a8X$((htdY?2g^o0ulX0$tGZu9bEPqnfzFn^Qa*(=|;Q4t+ zs47Ew*vgV%4N{9@W5w)cKe|J9A6(k0`* zk_E_?X0rP&a|(BYV%v1lnN%n>*~BJEJ-n3Z<#pDFMUc4^Wm7!1}=0s`4uQ5%1M zc|;nKsp{nw?jUj!o{;ZE+4FR1Dk)r)ZSG&&^-I5>8?SPqHn-PMK zJLO(z`PMUXZ+_+VX8_%{-hO1AkkGifMU)s|YO(A>N|4$D1U3!Y7G!1lRgNUirAtc0 zK$sqWWBU_!T*Lq2Dz1<6L|czrn-;ul8wX}B6y`=o9tBN?=L$zbE2$N%@UPEa^Yh;? z$I-yKX+Uia#XhguUWO^Wdzg+bt<&yXz>F`pZ&cQcoMJQBU7U08NlEp2gzv6Q)hZqe zSVj=z=i3qqVZj@=M7HVk2Ity3_$u-GHn_&hqVNb)Nh=f!@(fYY&D1lYwrey;{#?)6Vixj(tg7HZrQ>NJDww z6}~O^G`bPPw0KwH*bpb+g{jlI73IvY&?fw46ui!|m^Q(H|3Uj{R8099+L28YFX+Pe zde>Y(iMURi4A1}Nqi;w1lz3C^!3Hlkr|XLPZ>c#ZF>$wG3J1hvpfMGye`Ev#5eK)* z{KO5gWZS3$TOu;a5MYtq&2JC>ApSm0TW1F~_Ee}TWpFf9-gvv~R`?)B{jr7dnT3d1#hY{PM9*UN0bu<8ULV@wNyTI3ZpdIC4o31sxY^KV)taV_ptB3 ziaSWprh%VMyx|$UnV#Xb`7ZFkqGYk@|KAQu#A?_wEB_Vu69Wv0ZGo*w6Q)?W?o=S- z$K>kBV*8(L50Q(Bz4OWTH{3P4E(PIu+r1NQ z6-Q@CD$Q-ZThkt2Ab5d+21F!4)aQ+VHu3&ol-FdER#f$E#m-{Zed<2Ucf9_x1` zom})pEX)i;THZ$qS;c+-r(B;rc1Kw^S(1Jn#B-qX@u_Y2$VpsuR62@ouUD;OYes#Ny$xyZcUHHNS z?0F!GhFqC7i5P$HXAAxd?YChoJeURynwZ!U-?FiR2BKR4gGEdz1kBzyQ3pVXLIf|= z@7mgQ0grce8$}Glz7OcDGx1j2o34LO7v+7~P^xQ~o)i^QQ!Q753!CCr7u#S*=?FG%0MyyhsvYqH_4lCaK8TB2%d~c#6UA+x@n@#1?7>{XR#_ zG5kHUKB+L20xS;E{Oa2;NAe!=%n^iP+Ay7U)&% z04+2z@P7P{`c?)>g3_Jsr-aw8Uir-Lnw#pefSB0WHeWNiQ?X9X%m-fmrzJ#aJ8R?# zcac2t%hcjBm5O!b^dPBK)2+!TSL14_v(lViki6qQQ4T#aXR5NCZSPDQvSL>DsA?h& z+&_vj>|A}9uZ&^-EMnp!TPDwMQiiSyQevH)+fNIYS$y2fZx<< zWz-jEopw;kmblA!t9Vng7MffWa-FJyZ`9R5SVYp@P_Ha|=+ryIeQZlaxH@YVFV)88 z#H1p*k!AOedQ|a`)t$W3Jhd-O~a!EQ?z?tEc zhpoj9yrMV)?-}m48ViBuVC6AbveE_)mzG;zbKt`NuCoQBblf1WEY++W&wMs&%2@69 zMAQfWzJkYB8unNHOL<$}&(Ye`AP6k!yu%z_{LpK?R%5<5wg#{34$ON2?Si`f9Xes1 zb_!VgGYw5crA}aWA%2vY%11rO%YV@l!O$xQNL`J^7;OBwfzXmUW~th;ex_qQ$1bV) ze;znAJ>^-jn*MI99FtM78$jMs-8g3_DDAd(NQ*s2;LxTqbOpBnu=;X7PbpJAY0b zpPA)TGViWaNeznSW{k5hkIb(ia(1TJ#90Bz-w}&N{ zPfy;a;OOctVaEkj!SGtQDlM?bu{E@i^*8iBwO8Eqd!JZ^dF*`~N4CjwAT)w~!xFJ? z8<%ZGkDo+Ba3jf+QP;Mumh&kQ$nRm#%=p#aa()V~{E zwQAs0P|0Kjkr4B-YV%E2C|5hWog5izJ)A4~I2G*~emM@i*cwP}18MB16WkGC zRV0h1p28jIF${tAvG>4jD&V>dO~Wn0M?{i5uqa`G3~&Fq7t9-jr*ZRT4<6*O3J|M+ zYtv}P1No@p#go=w9!WX>yt!;MxsNyTbIkffAMZg0DR*o~Ol1V|ZERw6 zr^(z?Lp+)^8b{Z<7??AQVGr) zH5DAek*lJ-;ya%ceu}OoF7Y=S{y|?T0r%*$sQ z&f3`7Tis~|#0Cj<`FCI4N;jGlGzlyYutlay-bydpN7em$B%uASuVg@qM%TuY>)=** z!z$09@RE2vKmTF0C-3gF{FajB^mburL~eWFS2n&TP9DNd-5v$r->db+L>R&6w!Yc- zU6-7lX?|$B3-XsUrDHiWrweN3xE^kEOrS zE@>4{5`=;D4}Y8KdyTK)b!%kZ=XCyhBx{c=;mYjg7tN_NZ{$-86ZHcmqh3j86h-50 zHD?P^fefy>Wjnf?{6Z^5N(AzF!~M^+o2C zdumLS=(+PBwj=D*l6E@a3RJRqvJZyuy){v83rcweSa|<`mR6t`7mpm8v3eQ^r?j5q zS_hcdJ*3K)UwZs__={rXBysk}MtumlN*w`_2EP}u%v`_=A9!{;Z#)e%D^5Uj}B-ljeqU3ZLW373>(Rh=^akCp&zw|=5 zxFLwS2Vu8urwMLyNBDQ|9{egU-ZcdC@La#%GvT<$pQk71IJ=R>o0e-_1_*Uir&c?I zqT~Jd-dwE= ztnMuV@1;rxQiWgLYhx>-`!NRdg>Yd>L3@!PF4!e8q4QMTXQpiGDWD+UHZgbEq*=T> zn01FXeECScH`XQzI2-~;zpg#2Q(p4d+|LcJwcxDj4u*U5%D!WS2-fvZfr~21CxDC~ zvo=FWFT~FcE`TJ~b%pB$JOKYst|oD+py}^%E70`ekb*1Uq`I$@NGX6hSK#H9b4eDF zJ}_9DT4BaJWqZRE?3XUx=!$KOQCGpK%;)K*6e~Y>;885$f8g+laDpDP?)?tOp8M=0DrmsQT|8z3SuzI_`8+}1nop!Y>cxA3uyFG`P5*+X-ouU^W5S8_NJ^ivAH@6LD^1eLiB!0JHEPyPVbnTFgVUCs33v< zRw3Y)K2rnSY9g%fN_2I@$AFPpg`mq|Z*S+s@=vzJ3m?$Q!2&lOuh}Y{*2LZU9Ubx5 zwjhFzE>-!ZRJHI57%lnyd@#8CvF0mM8)|+SUZV*6*0nI7E70iN`P49b_RU7e*7veF z(+lxi;6NKDqH!tE$?dTR=)>z+evCz| zHPw6OQlIa5Z(FG3u8iNFGpR7YHj=4d4dtOS_Z|J4>darw_oSqpeNkNxOPw&k_4FAi z)*boG+>q4hkq)uBa1&+}@4Dg>)@&H7tuU=AVMq=tq>F#*HAIni?O{5(4jEi>CIdQs zNtV3l>erXL_gBZ7!rE3gP_0R}t_RL>wZ+vUm-F=xl)kFv#4?N6$=DNR9(Gu_gPjTb zpLe}qs$;#*gN$-B*VXbE3GDjlrN+O?z+a%i44&MwRiLy*2Gn$JcsdNR0w~kt`w8G@ zk!%h|l(x+^Gm&-qe~0ZpQMr^+z4*mL@Mq8T>(k%0*1Mellr*=3{y8Spdy9$7Rt?Bk$Q`kUV&ydL!6TL zbQ0>M%kb&eZ5@Ty`#%ZLdBJouX@-akyjClP*Yb97RZ*3kEhU~Gn92wyJm5_SiuT9R ze(^48vpMI^rCmG_%I=2uR{INHZvjc~o+#nv~ZTt$e>qfB~!FMc+@9O#S{ zt*GYU;HZvdQgmH@@Pwg!Yo|Gahc>doz=??RmfJKpxSLVeP-z(Q2d8pX#OfMa>x75L ziT6lVS+8vVMV!lHuc!acyqgsJ>XCEV_uJyOWrxBTfhNn&04ifv?%c})>Ib!X0qdk_N8=QylLQES-8;={D z{gwlTOc+;Je-}m7b2IGvQ{+mJoJ&mM=Xoay+@NtV9dn2?{Nvv8*;q=YP_F>S0ln5 zQ-!4UbZJ2IE!ys2O>8>Bp%VlNKyDa~B$yJ_b0`FQ3M38GqZxX>?mpOS+668ia zy4cUpTcaB=Wc6&vA3y^(j{d5)0X??Fk6q>zFe^$vPS(?TA0u);54SIZj(TzxMij(SN<^PQqIs8akw;b1t;p0ZeAW)E_%O zlJ7O86-vwWNa6Le3nF46Qj&$z>_jcb?D1kABN<=-NhM8gePe_Blb%*AWZ1>S|*(5^CKuC)mE^7=$mtMMCu5x&9!m?5)|Z^_jL z+c&EiMUA(MQVFS=m5n+ff^Ex>aKf=9^HEd_gBlDw@qY4{Gi;LZN5qIyK7-zStX$VX zD?y*S9X*{fQ>4bR8@~{RSF__Z_d)hoRP?N8r>udnHw z500}xvUGC`82_n+t%&lvso>g1tL+|si<87+2 zJk#T8dlOZ1CN+>IMbWb^_$0h?vw+Qw0Tkt%wfx87Z}ZaQ=t(1i(^w+5!TRIFI=?%oW#*i+yDrm4K#y(RFM zaxl6kq}!D`P1v0imj4puZ<(YQkk#-UD+G~Xuhf#e3j$rcotdZyOfvy4KVJ>;*spTH z>8HnPm>9}ToN1Xod5Dp}J@{W1xA*wO#9`95J#`ntWkwytA5}?+e(X_y=Y3t%T{HV` z{7Gs(oC=x$O|qbTqd2N2@x`MBuq_u1&Bi0G2b@ma2;|dO{7B@fOBk*|olUb>4idKq z{tuEssz}oBUVT{-gip3q0w*j#bM-lGs@mOKY=754W7Tt&WmKuEv8hRR@1@!;31Je6 z&XP^8wjAB4mekeR^xVT=?uujK*gNar&aTkgMm-I}i>Q7+@*JtU7q4Bu2p_vHku^4+ zSFGP=3^`pV!Nb`ub372l6|`K?nj&I35dH53!+Fxl1kFTYhDL4fq`I7i(Eb#528JWv z`<9rGWJ*_a2r^R8`ZjvziT?ivd{nppFW}>L0C^cSzJdcmsyz8GE|9bD*{vHG0-bAY z2c6>Cg+b6tMJ!`5`0U-HpoQwYU`M}87S_3$F%NctBF7t0C6O0jZIVw;RExA?ZHRX< zh`FBh?E}w^(W!Q^zB;Y_E$+63hQRf z$Ik)=uf5@Hij;Q8oZlI7d#vq+VbX&C&%%j zPP2q}?28GRAL-NF@imDLFEx!;dzl^@c38=z69CCvYz-sievHe29sQ!zDbe|{3k1>} z5>ZBXe)eZf+r`n|kQf+&^y{(c2fOX6*mqB5ADD>it9dWF;N2Bp;QdmKxvkiax3#Ch zSj7A1Tk1cJ6t?YDM6H?)a8lPGxGgO)uSEQ$$Mxb|DzsnT#phRpOR&^Ta_|8)52;D! zd-6t>BjX?YU)z7no`8MaaB4jn6~Tfn{`@p~rlvl3mu!&eMfKII*W)ZCBzr3*t$mEF z!I@<(_66a!9GV?UXHoY+Vv07N4=?l|FEzP-^g4Iw-1m zD5ztPclLz?iz**fWPB0Y+4*7N@2{UH9|laX?yaQ-6E$AuRrr?m{JxI11w5yspM}^; z4z-pB3_WkfZz(+%S3>T>Yt%C5z9SaTPOS0FWKUi0YzQYF|B8$T-aCQQ4ejHuOr!Ys(Rg8^^2BMJoC&!j z*T8j%n~4SJ+i0@znzFg^bHSor%O$P|rSCGi6e@MZ;p+#tG~QPtqrF|JtUwx40(Hp5 zidWL~#`aD`DW>RI4JMa&^y2)^4^R>(^RW}8aWS0F4?>7?m5MJIm1 z#yPXdfZW6%E&Q2WB`-FXIfRo_1s(+z&=DmX_dA!_JmU1BpADyvMn?L*(VPqKQPDkl z*WU)(=M8M-T8EEJHq+~Az_%#Ta(ZEK0T!UpSrBJ3_rj3>Ba#y)QH9ZqK$Z)48@(+P zE-tV6PVJOmQz}u;2;uujqo+S{ljmdk_6ZtexxWw>_U3yZF;-qoEcYl>^`YP#Pz(X( zUv-B+TsRTI3-()W*)f;np8^09?*fDb_eZO$9_z9&{^Y7bS~g180YB(Vxf6vcg!==E zg`uhpGYYqKkYHogD+V!J5SKU^mUM%>k3^OrN|iA6EilekjzYj(9}_P;ey6_|qB2`n z2N;JGHcff?iWExzP_B3mX`1jN?%;Bri;Vn3a{zTC`6W! z&vU7sF}Qa_C;)%ClTexO1wqsoh!l>S-k7wBb`xnEdr1nC`!l6pkFU*ei?VVv^6Kz# z^u;(BtiY=??~^K)bHN7))3Z!`(sqNNr7yBUvuw5lG#+ZeQaqLeiN?LS6rb5pJ!L)! zq;6i}m}ihriLY>vFU8p^3!g2OJ;PqASVkDaOm}ux5px({rL3G?*vv+$)e-08Zl>ixf#8`)2dar<(OO=G?k&^+et`q z3ARWVo6UiKpO1R=1+<-7u5dS;X_s}vzbT;bMp@L^`J0PP6Wi{)QmP=0XKydl(jFZ5 z?9IR3QDADDFvMxUud3=eJ-9DCLx(zA_=vi8sk!r1J)fM*pbui@wrschUZy;(A406~ ztUyoB-o{f;I)vBwCTc5FnXo4y?SsgcCkp>1@77UwlMN?RvWxAN$gd=~oW=z+iM{K7 z3Hnw4!l*HgRg;dcld@>4fa^gypftlXY(3)(I^7yk#9Uh69oi}*y6R_QZJoWa0EU49 zIF%@c-JuT1Um6IIhZ>V>13UOu#MFHM2xaW?uo8u#|IbnE){Az5d%XrqmkaVU~*#v0;OUPK^jZUPaD?jWRsLpn6)%L zHNB&~1M{u&YrAs!^2=OZ8{jKsc6PSR&N5;fMCTe@hSzTM@!h>|bS?yWeBJMYX)JHZ zrAX~pWWTEH2T7It#FXQ;Szj^61w6mZE1#`8*%Yb>hH4rlx_mno)ce?g8GPdE)6!&d z(07jl3~5)1F$nuBD9L_vJgWpSYIgd@+Z0tU>KM(mZrC`G^z{UA#=K7yl*kj=oO!J+ zo-K>#f4cBWD&*A*I0NH+1$aKcYo%MLHBK#7B+(;*&HwpM>k@rO=dLw5^G zHdz!Nmo^pIs@$LAlf3o=Fgv`Dt27D)Q7_g}q|{R?5Xh1};d)*+S7kD_(xDT^krf*y z3^UpOV^9akU_$<`l~RP!&_C6{<3bXjI9iIr){X^wHEqAr(nB<%5e$%@GUpfja@&Pe zOu=lo0IqCcT->C_#5r}`9p~0VREiJ}8l?WgJ$xEt6aEpY#wtB75$nR^c9TGU&*d+T zM%EyFw01Cq5V7}qwC`+Vc^(GM&Af@j#wuknEh!pMwhXec@b!Fs|F$PV&Rso1SE}@p zGpQ$#pn$>hSE3yQ5$S@?{I%~hwCb0#tW(KaFeV0Cd15&3(iBHPfNZ;svSJV%Om>Ig zfOOREDFW<`VYl!C8VC4r1T!Ft4~<6YSwbL*_Hgw-hfeU9qi`b2&_-we`Wg#cI!}0? z+DaqFk^SSBe~Hfkr(e5d+<9VUc3bHOgrJTyA#;rcMFbFx#Nu5ZdcoJ98*&2M#mF!! z<*NtW2+%GWX-P}#nXS(DKYcM8lBxp?+;rNTqaxVaUS@86cdY(Yn} z7F`c4WDjWo-Bnye?d!>K0ZGdQ;Ef9lgN$E5q@+b1U%lw%Gt~3SMD7p$lhdmI{`-m4 z+O^)icafs`v}duA{Q${ODnjamh=bh64(o&ic%c_>dbRGVj?9&!@R7t20l)D3uG49Q zSFQ&aBj0TJZ?{>HywZ{GN>C|@?3zbx%&E>H{HP%&(kf)25k3^O-^`HT5~%C_zhT@6 zo0_Mz-uRif%m|*vLMs(M&}{(szbJVd=zZD3+Z7z@WnuAqv?$B!c>x^Pj6rNY!K&bE zNsN>GS}r@p${aWFTpm*AE*|s18ry&k;XXVDfm9X0o2gqyB3~G5Fak)p2s8#tJSX(aYMAN3ey+d*_zHO|D3@#zK{Ezs=;f6o zD(_`5CS|L)12xT}mUWq0kUXDkK(=>2+5Y37sYMnP{^zjuK{j^p2oQa;dc=U4QnBPV z&X`BEc+an7{5}a#f5;##9C{cM@}7x3RS|-w(JtJ1mMmIicX|x7aOkSr*oBX|z(W%}bo?1fx)2ohZ zs3Rh-djNyS~`n1Y} zxeNa-=-e2fBgiK&<1}v_?c@x%VDitR2qW#w75<2Om`XePm!6&eZtztk{%LR0?++iY zzqDa&YTa#LbdnTOyOd_5!*7Y=Q}gc#ONw$hq~p3c-+%J5%!l@0)O*5_?z(CoaD8IH z>t6g_e3(mYQv1W1A08X3P^x*z>5@EGRUzcy)`D#0jcdn^`@mD+^ay>!8L%kJc;LNL zz?A3}v^unxbOkXz)C@)2_UG^wbdmsRM9Y+jw@N^;4+4!?yb0#hu8IM)cqbQ&&P%0{1g&a*^=w>7Yw$(u8MkQQWL4hR zCjJt#HO7~I()90R3Kw#~>!@Us&T<{%NojFoQa!GEwS(?T40%%}My)9_)sc(pe5OuU zz?6!RiVIhrD1@~DQpo^pB{!(Df%!Z<&;+1CZIeq&K6wLm&(F|s6js$tmM?Ilb?r~B729Ym6stvbcc z+eHJ($>9*DT93IKehN@Qz0RKY>0x*iLPHZGs}+exN5Ko*3cd${SD?X-4I1z_*;qA} zAa>6o8ht~qLqKR&O-~gn=;^4#0Mv|Bp-qh_K~Lfo;mn7w=7;&p=FXG3!t^iiZNz1` zSS~B;;Q>%1JQkLoRWHrwv?+Wymf1Ju7AJ$V#mhhC6;geSiLTIe7xu}2@{a+skdw*- zW;vV|-PVRn$r?&*F_OTT>FZo2rm*(F(Xvqt>91-5`sM0k0s0qJ{rX-b8JqFcyr#Gl~a#41H_sv79v<}Pvn4i%sppHJD6)Nf!)g^no!4^=u zt>i@xLVsIy?WHCRFzkM77i?@Chogq!eGQBeQ0GRP(wx6w0PK^;DdT$(GkP3x)ub)} z0~!~y-P8q$z>N=l%HMx4{|Ye=nn}1jMznDzwI3bB2LmjQOBEJ+c>cfE>@O7{?b z<&cPOfZ|mRFsPn?P8=W@Ib<~c0MT7WRR@0X`87jc0<;8PJdh2-c{l052`D-$Dz$E)ZQGD-1l5{A~ka;EiGsd{>A(fz>YQnH?FB;&l_;6{tB?k zT;+)HRGNV_R15W(bF7_6&5Wwnvdh6#{?%6;zF(e8vuN(aySKN3&?2I?Nju4sD$ zv$#w$7Sf<{GUUzL1lHDBf9HSA-oZ3)t)p-7U)iK24!MxPWsQiB8!6z z>ZGVxW$^QB-QvHwyB^HO#pRJu3mf9@`s}^-G=jr94p+dBQ%z_}$OuO>7i*kNML7sw z)LM}sPWVn*ss^0Sa8?Nl%{?IT#wuf2b9aINWg|BMpX#U$eXJF^fhOZ#g%X4O@p=i{ zqbxo^WiFhA2r&Mb6no+F$Z5R?NZ<=z_eb(7i#k9I>>bhdyv1&k1w~Fzi<8 zeJ-?#?r=r}e2@R2Ud}V30$lQWv>{1JLn<+=7PDBIC=pKJ!kq;tdfi&nf_L7PBC2qJ5cGm=Yjhb#SbB|mc!rvwf-9!?B|Hf_ zFmNANQGsX~PyTP7_6-1|spH^)s{fFLI92#HL5-8K3&17=hCSjL))s6RXH^l`_#Qqn z>j#9&Kwr0Qt>Fp#^?>%v_&Y4Dk+lYDFMWp}T*3>Sp+(6_NaC|Q0))n;?8X5J7R_NS zS*&kGnpESu8Y}~PdkFwb(C-$|qAPcO=oGsjlDR|^&dl6e7@_9{bj$@@9WyMy_s^dN zAYR4AkG^(JO;PmhEkuxLXsRUTL%=F^C#{%Nnzk;bGK?($uL=0!FUOnn91$>UqWjY5 z2iV8m-1#8GEp*G3ngBpVZxSFBM(n7g)XM>1#x;Qr(7>~9Pw1lnCN|h{_QDI8_!Vh( z7}L<~OvC@#$}s3)Q+lewIOk{#Ob00nA>t^#+s?M15HN)++mML;?`sG@tuM8;LNIpFfVBTGu3@c*01XUnkkc`%#YC0ttM%vQG)Sdb_7#Q`j1}Ekp?RfU~~)^J(f7fd#&kvNq7wfq&@4 z%nI!ufh4WYy0-x8$Ec61vu<;U%Ly6%ImAX}dc-{9jirtN`wy_!TJQOfAJ>@eWK3RP zNZ_8ZQz28V9iTbT4sO29CJ278LZVHf`MA`XZ2gI@H(bApBFLiX4EqTmKmQCXDS=b@ z|EvwVYQGQ*d+ls*Pv5cpMG+W4{kG~S1}Jyt|TG${pR2~ zMKVee7I-d6b}fHd0(&mpc|tLLD;!#0^{t{yAdNu^=q5wtLsn-|-wGoyyBmzAZRQZ? zx3+7vr*;TW<}`aO2uVm3GXH35vezw<;CXDC>F~?oRW;eswZxh*0wTTmXKs09?0145 zBuYi7a;aIw{zs4_>~8qiLSI{>)-wjPnQFq6i^3!hog!>NUs4?6fuLkS59rA{aV|wr znRVLRGl_~$3Z7LuJ2?mCG|hY@98uy^;KgTuG(NGM{8(yxk9)eQ&_7wT3#<=;{Oqwy zGe8OlTpJ97i}bowKCCSNg$*&fE%82ev?R_tlWM}`H|5$2>C=1&6_YN6Y6b!n;5dAH z=iCoMv!h~%gW{LmUd8clb4!m|*%$KjXnw(re?*~Qs;0f@&xI2GoNLMe_s5HBP~Y9V zm8_g!#5LmP3M_K`(h8wFu8jA-ay%`Hh?e5asP)WpTeNwveM9^yDKq&t`O=7mT9ech zp21;7z~T0DTIKY1(yekOh$Bs?Af8Nu!kdfi`y52yN5zb-SKYo)c#KiVI+K!1edPRS zK`3@Cz|6_I2BphT7?=>^7mB?D`_ZaIEz16UF*Y zVqhTKJqtQrv&<|@LExrs1B~rJRH+{^AJLD=qE%u#APz8|^z52MO92@5FgPVPvN$sY z(CgwIvK&y0w+ZW2c*23)ladwafh8A<>#&j|&r#<=5=xU77%aUo7ZCJRt5qEaasCsL~@lj=YE^Ni?AZN&}=bTrUEQjot-I&m{GJSX&9p zgUoTh2lg|63kJ3_yEMh^9soW3NF2yleO{V$CiSWd%wQ6Scr+zcU$OffK4e5vaL=hp z8O7BG{>DN4KuklR4XpP(d!h+5rT(NF(5#d?CJvlQ9qpsu+z{V#Ka(qI5APr*Gz4bC znc4SVyX=om+h8vR3Eje3{=RhqI^sXR&F8oct3<>OkUmEWawZ#{yz*oQsCI9lFD;f{ zx%@De3bm(}21XEkZLr)$#jJ)Ckj2VzUJsCNs2bi5?O&dl%(7Kh59kPQsl|{(9mYeDgEbRnJKp@b5q0_^E8LbHCQv19< zxS=$?km*AH@R~k{U#?xJd7PgseENOGF2q^8^A6oA&#pAzM`OCBGwPue;`*16jiRGR#7p+Ftk)eCh%=#>F6?3 zo(IafuYKFtIDY1!KvI7FkAxrrVO~$&^W1M%VI(oEUjmf+`r@Bc70%wgK2bFk$Cr=p z1oP@Uv$GT2qADOaChBPzGAItEfPwXliU;iMdx>37w$zUwRECG|LZMFP?(1!opT`}W zZU}vUbE&9SP$uwVat$9EJsIJ$RD9|kBJU8|UW1@bS*OIG*0*nuMWIa_?6L$V)(&&UZt`d8LFOXK0bvvx5N31BBlRdXix;((TDh?kLp|?54q!zh)=EEI4Jp%z*uh10UD0mEC@-Ra#YJl|ogy=FNKg%v@z?KM32Ex36 z5~`4uiSTTtwv)Voy^{1XoKj_xvnnOmkfsK_CCzYBT)|J2;1FbO8soU%AwB-04a@*& z1l7(u(X`rz>F$pVZ%1{UVU-Pi4^c@;amSn0sc}e_cvk!{&+s^V`&XHU&7PFyG>&)x z7p3@AEUb?V#GHyFkO+PN?J`nNG|lU_a`@uc^^RidgD2Xst@ z{~QONjl%0L2)5>}i{kNMPlmXGpH!|k+4(Kbubsf?JQPZ3r~sezaOc&Z0(<~1qxxk$ z%B5oT6?dCcKd-c2*uj*QtE;u?ydYlpO3$A^6F6e33AG1O#a{ez|B(hcd4+<|uRed? z3H2o8A}=0%d30M^@5zmnt<Q1!9E1=_a;chMQBJ-2QIuH0G9Csx&rc@>NzQ?x>A9G*Jw16BT+p1gOd%dNX z-1;;Cgp#w6=rQ)d;t^EyaT7X`zYxe`XaLW$$zvFzYZl;^wwhmL`)PG)sVybZ>2eI zQ<)oZbtq`IQ0{t%zm6mTX*q~f8qHux-yB293%t))Ec)i^2o%qgdRNEQn-9+%gGF%H z*a3fdhYHK@4Tu6&|%_Eao@6f3e-2FG|qjTwzx>_vYiV`%snGZl%!jw~htSKgD zOuK!-__Zh@1w}wz%xnI`d`WTAF;c*YmfcC_Ke$teqA2pjsi^-78Y;M)Z}7Ue_<+2t z&bGcaZYSo3Dp-8q5rPVPHJ6|cMZ7KCp?3*9at~%zYFgIQ)NsU@wOY2eh+ z$W>&Jl72%51%Ql+!^3O$pQ;(7X#>7|{;8+ABkrf9TJmgS4&mTINC&>}#vi%>aGS(J z{)vSxv_}swdKWfevRQ%C1sr5J?9SX7(J7jbrn9=)=Uh~PQ}4+0TR3yP^u~3pHW6v` z*#|VbZEL?88rn@sdM@0s72Z_A@&2>oS0q9GN*+8*cmXi+-9@%ozB;%j=&xf!QW%8T)zS z8GX0kk&$tUbBWi1A{zMn>Z>!9KRO*9+I*dkfr6@*FwD9> zClQ&BNL3xIUrltcyrL>OjhR$NIr-6Dd|ELsQSl6qYnic<-SM)9;~B4~*KHgMn3ks# z&nD)_^nX7XKK7*fL4rpf^Qqw`Ip$Ds?!@n{E7bzFeII=Bche*xc0lB`Z@Nw1U2x{ z{hBmmu)zrAffO2kX;DBH>q<7+53=@RovGjC`22}Wt8l_*h|s9Rq88#VxA#$NM3ETzaZ-4b=Z(H zIPqmb)V%kKwE-!OCVfqar=RB2j-hKPDJfAJ zX$k2R>DuS$eLw$a|K4~1_Sg5rJD-f1^E$6}t~}Pdjw}8_sR$O9Ko-ZvrlD}6zqiwl z$;9N{y~vH(%Wtpg(Q=dZ{;LT(;m)Gn*D#lYG8I$q-iD0hMLsF{l#@#+iS-Wo$X(c}lRw+%#JSP8El4DoC=d6(JE zjA0a!-Uba8)|UYkCGmrd<_g_@ zL;y`D?aM8x+MNQb%guGA<*B<^SnUsw1Oths@(k(2>-n|hM?-X^FIshK`g-bgu&|V= z{HydEz3v@eD^$>(deXwYT{SmV=lNG-+&|W*p|@3Y4gS=OnTKDSk&KO)y8!>x zLMQx)<2n{rKQ2v$NWs|R z)L>PdsgWySRbZdse~E#Zu&^Fr6bx%;aXI3`V0MZ5!h=U-HJi$0m;UT{L-gm61o%(a zUq-am4UfOMOI(J}5;vYqwaiIIPx&W7=Zh)zxxjijE{3%F15U@%#L+j2w`nssuSt8_ z?_HiYOV9g@FM!orn7{>zSfZ-}V7Ho1x`LxrDs3e;r_~)drpi4xrxkvGI{$FMEiZ$G zCDYd3MzwjDP(kRv+;Z~=zVjP4j=4cM4%FVcut1L`h-YHkV!)O-y3XwG0RJ(ND_y#s z)2ELC@zJ{Tz) zHRbUvZ{xE8`&a8@`QD?iyTcj^i|+y;SFx~Y9xgenUTONxYGZ9pN9Vg0LL0hLYuWof z;>OE63``TyyW+o6nEjg%4iyxd*=uIu%|FQof@#?|>TT|TljwSLdo1LxZ`j3A^y<&# z!>wuW;Dxi~b$Y4-0kp&L^cGp{vg7xIbNhwp+nNfm(_dPxQx_|porLoG8m;i#%xY##DyOt;d++p};5#RZq3|JSrBC)xX`IW2gXTKPMpEFG6i|xu6Dhc!pi~hKnh*@+-%6-1vO|a<( z==UfK6oOKvH!p^+f5g}sdr8$CApVrLk;0iPkWtv9^dpq+112gGQ`~H3hicyaJWI5B zx=DO-9{1rw-$eY%jvUwr*W!G{MYpC1{LN*&j>ay**{sg5u^<-rcyQ^gM+eJI(;m#E z=k0J6KT(X*JXu82`pu*DAy`-$G8S7ApIgEA5X?bUbIHwF?hQeqD|Fi#CKqHFour7e zJ0lW&-XHC+mitcb?(3ls;3&Uqh}gERa75nGym3?O(N^=7n*iduH~wYXeMUtmr=L#j z64*VyiU=B6mH6=C=L?{KT0-UD9KmI@gnnRL&Hv6D{B=I@3!Vd5-y;{qBI)HkWw0y; z0v!H7zW<2|=J7)o8!PLkA0UbWu4Ri8?CnPj2m)BIZrI8P)NPdazTBSi-5-I9dNC+k zZcWw6%gZOjUZ$A10NPd&2QkYZfO{67>S9T>c|gQxa&ji=!b;|9sR+f}^^#`4UyI!} zv*6j}8``?*o=YiTziPa6K3`Fip7^wz@M@$G>dFe%8dqU|zbK{wg^qoUmYNtE+5?1} z$)Q_y%jqZ-s`HLN;Nad>a07oAF|C!k=`(=++|8{rJ3G6v(HGF9M$$hD@Vq>oy=>g} z{pM|4toIFYn)dS56LLE|K%>zjjx%RJf{4@oFOHvf59jN+Z%wU=MXjSZn*G7-ZjO4| z!O41wW|~N3C}8gtw(Lm=iK4syX+p~s2L~tW{#u@nhWO7;=?mwNo!Yt>V%*d1<`3NO z_|00Id{1cv(p^eHGesqy%`>_ljjnc_rGH9W%LaH<-%9ymJxD(uy61&Z3pCr_5Me%{c)510gY<< zy=MUj*M%~YCqgm=fYdutNY zKkxu_u3jRDFX@t};K-ymO4~wLQf*Pk-I{*$iL)-fyzJ>7Oh&Go6IIE^E@6j9B^PTY zO*@FY3Ig;vvZ{Y}i3Mu;TSq|u)+E!#oaaF@G&nOO_27Ze!T5{q@rq7v{~rN2yu7@= z>lN8q+1sDDvr9RDrhWmi={<1C?e+D&9>C5E2dCo|O$DfB?I%UHcL7DP*MxPhdin#; z<*)CfrA}5BqNV+`Bio3j--SL@QUb3=D}B~^lPOi)b*bP<;dhj;&QOlJQKQ#>t@8pC zHSx9azk7QX0vK4C(?sll(VMtW!*k!cgdZL)OJ5!*LZ7ZmN*#QE23WseRztXHF{Xjp zyLW^4;TC8`ozE;dFMI|h0|b1vKLA1U4!tJG@87w872m8Jijq398qWh}-~)c<75xq9 zaJ28nvzY~nt=a{^MSDm;3&@F;=-|493%6tb(WJauKtJ8QU+kDNeD=LlYH53clY=%L zF#jecQO;%OrhE1HpLH=uPx+m0Hr)ua1ZKYc!T;ih)XtUaow82)deHD|=u8(dqhtPX zw#fcX>U87#X9EUI#sWSu<<>(vn0sB?->%5XPATz*xw76~TS-mML3|?>jX^5{G5 z{E%&4obld1|0sRJT&-cZ@C@>=h&b*8GF|_Z)!fF;WD~z*gBR};QNI7$nwD}kP(1sI zN-7hd-K8`~Kd_C4*29Kn$$VR!z?#1X`AW_6;&{1vH`4LJ=|+XUy*-ApnXl<+9m+9cA7@H5qBM%lf0qoi~RcN430|Ntq)c6>ft<++S{DWS*toUzP zD)BSFl4<}6?T6L1H8L7PQEF=H;|)O83<$FglisQSt3Zow7#`szKz`jXPYej1n*mQ& z91xZRpg%{g^XZrVI9iA<(JO3C^_#mmKgIY$uraUZwD$Sc8;(QUPu5pf08_VLhR90I zdr9wV_p^6$A`HryAO%9x7itXB7g%`4RbE)(*nk@SV2Qx1x z?ZclweE2ZkV;+ym3xuT{k(C7%Y~(^|S+Ik#B7+51dN(&W zl7X(SuAQCIOI`h;XMojM8f>H5ZoJ&I<*LD5)cVCdRrArZ|HU%7=7U{Olxx$z!EgA- zYAm|?L}$;=0r_|jcL`82!SmLNulLt6gwH@BmnF|Jw9f{87>;PbMqRswUI!6FLm`5a zHx9li_SNL9!{lroJg47^AecRDI-K@georjM zKSc@{sJI}nL;0GGxo!m`M%K)kQbq6f_fU~dJzM$vwHV46qPqEf1UE{*n=(ZDN^x+W zUgtaFH5-MikFUV~+Ir^I@fh;N@V%68MT_|A2j}57=^mj2G2UE5kYFv2KUb4ch#?Z! z$`^w^O({3s$=9Q*xc4YruS^2+P=>MNPn6Ece`VJdquOyiwR|Kg*N`%X`JXMD3dRs|g0J)@S^McFcATn@&`CVk_|PP5?72FK z`%8Y#mA{gqAt4CGZ%u!o`zC6%7y0+%0@&q@!d#FS$1Z($3r}?bjK?rbgh+H5JKk&D>;^$&P;S^Gckocvjt?W8)(&Ho0scQ2IcB zi{RO3=bNK8XocU0{@)%&sFCwi{5;6jmw{py%t#5aQ&s>AdKYt%{#md}`(+|IP6*n( zEyS|oc^^R~7-Z+XAv)|>%QE$V1RooQ9EAi^5MX{RbR&jn*GyRc?Vr0L<{%0d4l<*w zDRDf$*ZGdwdIe>4*j{9iD+sluOUVm$3VR6cGI_LkKir11AhbxQ=I^<@BEuRdiC68& z*Ugf4)h*`RS~Fy|wLMEObn6Hqg3@_!_@wmhuYV9bS!qrp)Z|fAprc;HN9Ccod)}MJ z8be8#x&G=1=6IcBtZ`__35nx4?swg8Ben#$&`D1ls#hSKyT2~}I>?l;{HelLh>Yu2 zHlpzL+Rn%s1;H4cF4vTka*2%TrE5yX)xUDKq9~4}m)Y&N4srC3rt3UyuDRpZnuw_1 zG&GVuH#Hh7q%2xFg4I+GmmFUbZ%*6F6Rw4u))`f3ZF6!^+@6OcOteKozOMz#)GG;u z9<6-53u^iqkQ>P{1kc`#D^G|18?iM$^L z9ez7(KbP$m^wC^YDIC;qpyme3dsVK_sSS2l^UlSvzdbsV_uW1C*$2AG{E8PBUkkGo z{iUf2ghKcmWq(^M4MM{E*>=Sc`+EZrp^B!i)f}sEe>Dx>`MM&$W!%Xr~Bz$mW+?eUZF7iQpK?d!pVNFzu>4{vmsWxuskesBQd>)RV8olgA3{H9aC1L&DTW<1x`!fcn~%wy|N4DX_Hc^2KU=iDBERWeFt zMDoD7-@90^n^yYzwqH0~-JI=MqNs);fe1XFbp3`{`-&X*tuKYFFAbD0?GR*gDDIlE5Nm`xG15SOan8zA&PymR)`ResxNkFj`xLK?f^G*Rn3 zX}Ud-yg=mn=;^A?anq3SdZ3N85er@17^3^`HynRcT`fV~*MZw{3u5!o#Y;V&de0aB zGD&cQ>%GTu6Z}3BH^-*930Lvif3F#y-3LX$hpF#fJn+fI+~=IX*HKctemC^=)lYRQ z*5nLa&G1fbD_X5M{S*u7cbs9xIT_r~zcnFC9LMq4zdUpAt@5WM zbL6;o3V)C4j~2{C{kZW-?@neXeedHj zNR(g>x0t0_oH=p?vSxG74bh*W=J%GOhma=SoYBqrug|f*GS2YuqF%)|cwg|d7F2A; zLA+mkM94$W*ay}!mT<7_#CAO}fY1%0vK_}OupUSvVP+>q_w8El!rvoe@MoaCuAf7# z=EM@MLfSwvP-G`h-EA2gg)+qN^QhN6oK=nI&~VM$u3uJLPvVGyVut|1|i~;YQ^dBwk*Znj}lF^^q(#G-`1{HT= zHkJ$Li%`D6pUKE*7R#RuOdZeF_i(u0Bqh;R>Mr*dO;^dRUS_J4r#wBHqpzQc=G_pD zpdh&U)kN0i1@<2uO1RdAL)0-yD940{w@|%Q@8ipkrYC}9o9IlKAhm1cy$=^TDa~QB z0f!zpHa^)%sm)F&rNed%-Dx;)sy;EHy=QrgJ%{p9&nH?W=f|TwHF;#7Z0-Wy-E7+1kV%GIZrt1j;GIhG zJZL|2ynbFvQrsN)rsiw72I2ik3j9PN_~dr$((vX4L*F@MZP;D_58*ORc^SqJG}5hs zC4Z7)EXDL`m8)g1j5d3&e(^gy|V zG|L#sLgXMDZV$|a_gX8pdH&oD%Z+kbFPcic=*z>IBQnvgQoIS z6?r5f{6AXPoX4YGlM)*&k7UYn@aB+5W}yRh{dM8}f8 zU*Q2Oc{+}cxE&jgm@r-{I5k&t!f;>6>SD zSjx(8BvR3)F`S$Mu8&%*&Ill z7RgCz#}F^j(F#ciFVuD`&WC)IUEd_zzqoY5HlCdIGVyB!*?YCnhgi5@h z)1N+O_+HP3+S=CI-rexC^iks-KA%gI7lKdwJ?!ui0^9edDG2h%H*|PBV6p?+JRSz0 z8cFylxHd{~@d>DM7p#eTRKZaKKr&$|k*KWh5P9i- z*!HyRN|3@Mt*7|e+()T{7x;;2x{3Nu&9WVPC*gCs?B3RnRS%mWOe*!Nbs(#pF|2T| zjbKJyLE2Dh!LZxf8g7`EZRyin`_kKm_EpvQEbcE7ky+f=_le+&4KpdJRden4JDAWY zt=DqaaBOT}n)zXScsOE$iu88DQJfwWH!bakQnyza%cE9SIhd>%9e_Ipa;Pokx!CI) z4;7d)?-1T3qPul+$5HytNkC(8qSeuXa^5Be>Ji#JEJhZi*o=OUfwBlPiFemMnh&2ya1Y*KPT*XR=Sc)ThSIGErC#$K+lKoxjN-#O4^Un)&*Dv=X8gkP_ zid|q&rNLWVjZjjDx6oHDBI(erHAxykGynD1aK4DE*$zs|Wh86(WX*v1g#}MLJl33a zguAr`A&1ADVVtE8kZEP|?hv2W_&*M;j{t`;N?z$sqm`-YlSdIh3bX`0h%3>pESXrZ zSXh*&a|n$otyl3K>?X80^2debkTl-+=_PKxi(==`<50`wKD*HmaC8D|rtwd)=t4~3 zOPg(2>2nkrB$P^bF{Qgnzd^=f%x0O|0=J$L1MOwjW}m&KDoIJ#gDu~%;;8{=g)HZ* zBoGN>%2+pSN3DL7ig!^;veqrlBwsB_2O$ZIZVx)cQp{t~D3Sy2_&aO}{Gi*dS{%rQ ztLUp(F|d($0IcjmdNVz`S+AuH{j%o?^{}4dwI=lB|FYERM&fJ z)aJ-}kho!&z7ldnG?7=(_eqTC62KY#V^KM&R+WtF6LWb7MRK;Su`9MA+1TEXg@(KG zC)wK5bRj=&UdRpGlo9p3gE$!guqunQfym%BdtZuR4mAZq+=*UF>oSS_sQB0K{`9=O zH>qw|X5Km2_E*W_ZpiJ2FxYPFK#xZ8W6jOTkhh%PQm}Vi&0U}trb@!q;lUU-1D84@ zt(Z2U=KX@b!un3$gAZJkEZcE%*TkYe!oFD~iB}Xo$CV}$eSD^?e3Y*S#VwRIe5Ixm zV@|3P#oQq$rraad!NgDY9t9MaCg`>yY!0KiBw1(Wj1o|JlUWx{K`U2cpM;yQ5!bE} zAN=;w`pyz}N1mIuGAol?d9@q_-D8I1g|Rh!)e%cl5d3kl&xId_W8WqCPDK`;)w=3& zHR$n3MA4u6$p#0Ub`s{akEiMg{LxSSYKH8r=Th69#Dd`nJ(0IXJ`)-v=s;RBAZkhp zIS>v0Mp=!{g#<(kC#6HFc-c=ovvw_xVidn>8Gj0l86lSqf_5df3Y5cv5tE;!2E^qm zK)E!sf4yq9%gUw(C8GB+p)_n(Rbkt!SJ@?v9&;lHe*B+bw2DyQHl7~Z6S-Vn>RU(G zYbHDqx%XBR`!XNN-R6xCyF#NugO-~OGl*V#Z&QFk$22QLb<_nWv~KiJu{O}Sh?V~Q zbK-7_KQgV}Icszd#aSfjH9=Q(BkW^99S%|(e8rSTBH=!n2?^KO9bx&2koERG-O}i) zIwCpdqVwP_T=HKknEct`+3RLtm3raWV^V(Mh&=bEoUfLTGluO!a(u7rf2`2-hGi1b z?UA+DXmpc~H{~9msf?gd)U7&viqw*L7N^`y?#hRQgOCj!Gje5u7)}u8IiFa^=Abg( za#GIQL~2aul!k+&Ha4?^A^HBb!$o;(%WDfZB<6mPiZuK$e~G&W=hlgr!Eqfqvc1pV z)wJ|`=)y=H;sk;F&u+PytjGdup{4i@1;q~j>y0Is`!o9cAz-7ZBv?424yAQ#afcul zO(c_(peI5?DiIAWytB4;;;^xw7z$29MJTAY3Dij2VT8#>A_*bN8R8p_v96F$6eb$yc^#~s?j4pps{jAX_5C}sn52)ewpP~*qdQv z%D#AWWHFBQ6#-V+aK)??_Gyd?XZ+Y9lt;FY(7ZRF3-1Bw4-@c@0(l&mY>6#FOGFo= z#Tl2C0B4xyJs%i}*-qn67C}tj?F<7SCXa&)hYnVE;>+@ zW2JMH}&Qab$7FO-XBxoyK10c0>3JhgB+X4TYwf0}QIDBT9=y{!h68 z{WY}9!6VWo1J(M`Nc4yx`A-y_AgdPF=)8uVZC8%ecIHZSJk=~5A?|y-OJ}C;5CR#T zZ8pxN4t?)Kx9g*~F4L@M16Sni)`DtXfl=}p!dfH~5psyXr=S9T>rSHr#a*La!;ha~ zHClXC+Rfe3YWa}tCL%n8`+@m8bL4?y>4BtL<82lD)VG0`%DBq$pMU-7(NR6`oHGC$Z8o+BBb0qLz?NJQJ?s}u$ zgF~Ea2m|j^3@pMSm%c{2zmMG-Uz^6auN1Y=7ibPc(pq$hjVWesUI3T#{L5Q%p<4{2 zZ;P4alM(h8_?v@olTfA~@Y7@rUlnnhh9fS%2AtHg0l`G^vx>7pgqFmOs=k|bI zU-DqpL-!K}fh&wr(CQXG>z_(8O1m3}>DTgm06u+5u~n!@_v?$vk|l6(6Mr_;@XUPN zDf+8MD2@aaPNa0KYpQA5Dr4C_K^bu7hCl;5Gpqf@2_zWzh+qZZ6oMFp_^4a^i)8Bu zPi^?sNsFv&bnXE^g{Q;wJ@TBf>)(51 zGf{}Lf@6DTDo*MRP7w*a1=}OeX@*Io6>PaWc6g1tzgfmAKv~?C-rnk#`zPKGfE(3Z zh&>sAr=l=cuW*hn5~W>%%>UGn37Ro~3WLsmh=K?xn|?J*#;g0s{op|5WP%E3=Z*}N zA~vq;j|Zba_@Gx9;qBGh-T!dr)r2nN#i+|ylh^N3-Q~=Z2q6SRcfI@0G^*uWXa%Fl zqplF$j7W(=G+Mo8yltqf6EbH+`q~WA(GNL}2}dym%UvPR(pXRsuj2ti0QIr-yC`Ul z*4^PX{5=YSLhTyFdC$9Bn8c}^EK8pvG_DJWNuWOW>oBE#{pm?-O25LqWMSI_)9OZ9 z7%4q)KEVw{^D2TO^5axAa^ctxAf&EavWGKGVd8y(b# z9zo-kBNnBIict)8NgxF3nJHm%(jT^}r`)=0FRixu`XNXcWUt&uZjPOqkZgqQ@~Vfz zRaTP8Fvj(c_KbN-GL6oP*8+bfv(|*L*_Ur@oM)a1y-eS`UO}@KY2mHssA`(S+h?JQ zgANQ0>{NfVmV@UoXd(QjG+Xq2m^xng^&zkR+ir^VZ#UZe=yZ@timnEw;B*g%{oha! z^gG!NLYCe?9E1d(grHlq+`B2oOB>eYkUZja(X}H}Ux#*s`z&X|SkO#%`~?^wEBT6L z43PrGJ0;!Me6L~gQULf``YM^soA0{}F_C@orQqX-eYr_J1ItSW+>)FF!qt1Qx$EZ? zWPSXjB@Twpti2(?hkM);9EAi{lg+;s7=J6-a%wmNFLt0sw4is=Q$oj|~noPM1SG3~dcQaUlD;B#Sh?r!=tZiQ_Vfyue?ylH`ZAZznq=bZ4;3fUp=SPkn1h`T=_NMtF{Y~C?~>G*4X9tJ9w^A zl}tTdPJUEzg{TZ%zKDmQkooefqZJ=EXVPbAnD^HbF)^K!LUoe)FLcA>B0(2@b=zLw zu=XdY_VsUdbDNXlgEyNp;Em>LbFScg3f1n$bP7n7iWnEBaSl6?`@F&DVZ!9)dNX4) z{nkG`p2e`TQ$eh{ev0$w9Ho4{ehj00SrAv11aneII<9tx zBQi_?Vr<3D7{&PvW+p{g7yYZn-faS$mqV1TJjKa?$;*KODo~FW<;GH|>zwgj#zm0UuO-bW8 z-@N$#J8QjBN+pxJhSgyj*R%~q(bdlm8oUSWoVL%*_m{6V4Jq3@@p6FPIt{(FPx<{N6) zI-7u`uy!mrM23B>2@G+R0}z7tt5q@-^3X+jE_5JB0C_(BClP)?!-9@P#`v=gTXD~*gAA+Mx|Ns3W1MmnqEF_yAM@-R{XY}f#Y+ROPk;t{)) zdv@Av$=KR8vTbGmm7s-tGztpawsER~%*E}&9z}%2mi{}`YguKy1o_97K|Iv=aekF3 zNjafgw~a2LKck17*P_QiJZ0^(b!M&;4u)|TAb8T!GAm(}Y+Q;jWr3U9S`zSYEaJ7E zbZW+do2UMdM`V3e)~^>-iIoz^(7|-^J{4eKLSR`Y^KgV|7~#mAOxCnS^mb!Jb9+JJ+$>i{I6*{^%rmnpNQWkgcapWY@vbgTf%0xWj* zJ#9$0uX{0y1WKMV@2$3R&aiL{!`0?dAi|tU+Mo#rrP6=3xSq7Q71xuR)hPr}s6i%P z7$Yi$heEZGo1=)ST!PMVvfA+tt^01RGZpy&b9A0YC$q~pskB3A`^WwsBw=|AgPe+* ziOCmS){d*IAax}qELvMN$PnpuZ}p%(>uH+TER8L8C#~eiF^{$}~jQ`@nbw+PH6ntg-9Tp!_Dr`5ofnBv54QF zwAjmCZLAjlT|ZfFY43h`+xi;H|NOI}M3%0bVk3=XV+&eN?V}rF%7tZDhr3(`8e^5T zJShX^pjloiS7y}5>>kh>oqYV~Uoa(S^`vOQBwMU4IACL)<=WNm(C#)?#RhJ6|so0~x8 zs?_%dm?Jf7=7^D=bE4GudXB-ExR#By&%fP{j8f;-Ek9e)1Zd&Vnv*9Z+`Qz%u_q}x zDY04PlNH0!Oc3|rQDsB4+{LV z!FbTh8WY9>O@*%)=O(O~^2D%v)`R#k6E4U-%bxH%7d!i~dZBq_Xb;k(oN;JY>sPC@F}vZU4c8w%i)j)0!TbwL(}-C=NDHdn&ErRT=3jZVQYU z0d|1lsgs|{j@&w5<##RQZ(1cwcIMlLIN37cnz>gpD zl9Ky6Ga`;pC#NMlH;5$(U}ole{jtLNllwLe`#9_P_dtK0f)$>2)z4mqb_L(yk#)?g zVZ&!SJefHT6`DxyS5GKZK{=Owo}Ga1yT|Xe*BF%AE`qOO;Z=~WPvqbEy zt4e$~cZ6-Ta=Rd3=d|p`NtorjKQ{XxzZ0LsWo3TQvU5X|zkL@<65G2L9uV+EQt}QV z^hBDGv1Rw=zNO$fvu#0zx@R4a%cE&FRrU@h0(SY^)}S-zqQeCvE$6YK745i+&V1b7 z%xP-0_L9p2K&}o~F-ZZ?ISVdc^A&vAbEoi*Z9jL+24f>Jyhpj3CT4k8zVn$+J>k59 z9ZzJf$kB1Z^HU?l>Og&T0!-om78&+*>ujq$r`liMRH*lB(Gzp+kr<{_n|bX6qjZ*f z8<*qosC&i|?1SYp3&gd>JE3z%o#S!UM2EIo>^b@6xeoD9kXqR)|K(o?A0#mQTdZT!ddzluO@U(37jBWZls*^95cmY zdL^#JD6akymu(z9^J=(PAGmh@#|70`=fgq|Lsonpznqx@ zKY#Lg1^?0p^b9h%LHB|bpS2%yw_&GQ;=U9W>i9FQDBQhd=cdC#iDCmeoeDHTUYtVZ z4?NR~IE|W3kbJSDpra|92LEDOgSx8cIXeXW0^`xK@2g0kC~_dAE+eZ~oSUS>If9jFTVq zF;tN`v#C6SQgCSGE0-ff3VmqU&SdL0ZkANRgUyER@yE$8~tf8XFNHpvP&3W4mE@r4GXtl?6#+D_(p{e*Q)Oo@etme@G|K?o?=UwkztCsfq()k508{t=Xq%@(=o_|^ia+z1O<{ZgY0BjH&a`$hGh{bCYj@=p9oo|E}XHtLN8duWd5*W|Md5)laQhtYN`Lct{GgyhZ!Zltmk~4*Z@G&>!~N&!t6^QNG{xd~ z4uFOOxYtHJ)`>$5OlVq>?J0ARQDw`+mdHeTS!wy?nuv#IX;-t=roV!=NB$%Oms}rsZh7HLO2c!jgm}i+KDoC!lI%=(r8&;|IBMhP zZR(ksi~W$>FRbW9)fC#=?Bg^fD*{oAbl5~;H;QM!5#5wptfR4+&QEKvRZo`!NTSs} zm2JIvo*Zt1WW{&qawG)yjXJ_|!BBY0qx)hqbSma*<@A|^zj8QuJbSG^g)dlCQO$I5aneWr3Z zTOPf>^K@7&+x#!6(zGdEtS9YuG&dXO%{>jDe!L9^Wj(fK$Jz=*SBUTEe;*YYTsx;& z(O--c+h`HZ-g=7SHa0LC`+0(|OMBZVujcUEwUI>sIvoF#{3$oP22{qpGC{8!BFsQm zX*5e;zmU6~p+oMf9I{m($$eJXp$b|dR`l9qgdsxqA#V39W|K^e^2tY$cpUR7izO5Sgskol37+Q+?O%awwR}$&ORng4Nb!3Y;ac z8GtjAk_0x_9#`h{%WZ{4zA9|tkzg|Ix6RuUi$iH0aPV_nK{HwM5Fl(4Pz)8co1FjS z(SRH*&eyowMYGk*IBHQ0kK~ZQcA<+&Kp+s2fJ$XVX(3W36z_hr8WgBu-Z7UGc*O~EYFde@!qA3xU8)7_6syn2)1)6?uB>+vmL z(n3(C+__}AXIIPu9EuIORvWy=Oz1}HaQgqw8~1@HvyIQf$RVUr6-R9Y&)kPh1I!}bT9ZV2)0 z0YW9Z{rx)E58g{D3uk1f{y|k(ppl?(xZ(Sy`6nN#x=CZtzK}de3qt}<@vT=RFF$@U znXFt5NKYp&A5Ew*GCggw?5*+ko^5&4!uS!FmHh-qL$dVbIqR|(N0x(;ayeBTqL+yP z7OS@=LaNb8uDetm!b31Y`2cQY3QV(n>`Bk?CXY5xDi>U$h&tr81dZ_9;El;Kug$74 zQV3@wlA}Dp(vGggcbsWMTEn}CF>{RX@SJd;Hz1jj*Xp(zc`h9uRMsrTi0+zEGOtnk z>K5glF%n~JfWv*sQe;U+6n!ieUcv8XNPP&^VoFw<47`PimLD8vx+5TbdIj9>+J# zk4BxVv+UadvVE_TZ3r@0jXefQ$nDLX%b%>v2F@w*q2b1Cb3-U9Bv4ynT;};BI~nK~ zV`n1L^OfO&nx_4rxm0}Vpm9ecYm9fQ5uI{;cYZ(8cdpGok||W~N=tq%4^1}rS|aOq zm|653%2Hu}#zM~84V%@A7cl^N*u%6vY~Mtq&GX%l+9=g8);yL9gIo81;iSyQ)8yi- z{)=Jioe0WRp=%3miAYoIXwJ4}zS%>`2Yz%mE1&nWBoYlJU|YFg2O|(`tH&AWB#U&H zFst6A*Whfn?tV)XE!o^kXGX(F;Yx*xln3kh;luE~EPa;xK1ZsgRebV_z^EdXvQj;0 zD6r&Uv78ulM*Ea>)J_Q{cSj}%`f6$mj$JYl-mTMX+XmpqiJl%|kDK!g2xtuZBnVXm z#iH_#sfTq!CtT+R;zNy2HSclXJABXdC_-)}{Ny<{Qmc$JvX|)*OcqoKpp}o^9JWSk zqn@Jw7jT&j#Eb~z#`@&ps1q#zs3~QdYpIh5#E%V8rpm5|BXRh?xj#PDx(1u0tW=F04)$z zw65n-(+%GU)Pu&HUAy<+SPPf;nux)Cc^7+!T*OaK%C1iJion=7t@#b&}{ni&Uf*DA0O< z-VEuD#CZ{TdE;NNS%CWH+K)htbLUTxPxv9piVx9&Oz_B|-K#+yp_Qd-(6?_PZow20 zztu=>`~6_Dq{fty!w}s&>~4yA*=6A9MMNmLcwuGE3y}+--`BW1o*!)v44mG-&o@zJ z`yMI)=W;*c?TR4-oiV00WU;&gvjVTeLfAt(nnWM_;W-sj(r*J_qNINVE)7wG+!5ra{0)sB5;MkL@{s|&-~X!@^K6> zAd8moJJ2FYB87vFTA6l@k1#>_sj{CweF~bHbC|&U`eQ4i+23;O(;lrkb>tYtq7g z(kIM#0Vw5oi@Pi!i=jk=_L%vi&h692B!C?E&s1al#f6uF(O~tQAJJO7(31m-h$BZw zPMw+?I9qA_a}T!57WxCO(J2V=)dubTymFerOSOi7kgc9F$PaTKer#e=@f^U}2=U$G z((<|PONqJY`h&g#|3S&G40S?Ld68Btt#|J$yw5DcxvOElHH8=_>Kh|u!)32E^ceg; z#A`JA(*0X(CDRJTuA8qv`8&C*PMO~ls1t@6I_i6VG#9DCfLyVAE$I!eqqckva13lW;aW)WAgVEu?Ml>xj$kQmRI50 zz2ju$eX8i!cW@B#)E5pjn%wE{7Zn^NK0Scg-Er7=vVGAQMx8QRwb!_pD|JlA)G033 zaNcUmME#A)L=e*VqmJe**9ygWmH zif6_SxfiN$&H?PNGM#gxh2)mw9U6jMJeiyQ_Brwp{xW^p+d5IEA_G8Z%3j5o1+GvfGX)nM_IS2vG2I*~NGZgD+(iefE|zy`L$#jrMN9#J z^)J|R?NnViTFbVlJyjJN3?l4jbxe9mvFnHrh6Lus_A6Jf-6#-zF?x<=%w7CUHjn$| zMr|U5u&pgs(&EK(T(QwzJC~nYEaQKk0x(>Vet)##_}TIFJnHySnAL0%IgsM}^G2mj z$qUV@mtcI`ZyIgMo?aWu`=%!Kq_}~ZrV^&a$_$k^1OT;awC5VW^H_B@aDMs#ME0Q- z*5MN5T*Y^;I@76&*G;H59Y`MEpaj2ed{4IE<@w6#DJ)*S_VbgLK zO3`#y{<-_dTpf*m#_ezx+O;=t)ck*=#x&BJ50B!{%HQYq+4}Ai58Y|nOsu%e?<|p* zJFqibnb|b2U!&}e?QKkCZ3fCuctUJ1lPF9UJnxVtOu!rg&ER;12}5Vk#r#-Xo)$nTZ*{SUr9YE5~LI#h>7r zX=SdLE+5~PBdg$@4_cI@1d6>oZ%8?qgSR=+j+U4lEQ#L5HxW>q-z^~e1AOzlF5R#c zA60RfR{3hf0Xbd?H&p6|Q68`3qjXfTt~BH4fQY0F)yhHvwCt9GY!<>2-P&H1!ECF| z^Iz7pJr;t=w!Al{jW#zMl|4Au62!zj3P0?AK}KqY>lU|D=!j&sY>bQAeJ`~;uzM=r zbsZ~$%5K>jry5zENK!KQH)|r;prK{rT?+&TG@viSmrwy(b`n zz^Sjh^b>qvKz@a1A@4mN-K}dii?|%I;|Wo~ z^Dzj;xMgfUs{d9jF+*T)D?l=-Yk4f_5XIT~+zFESgsQJl%GAAJ9uzPco66hGmy>M# z>MYtjoUTI48LL}_Z5y7%Xu19> zFu-x2%&^x0szt-CLvPYGSj!WG-lWYRkDJ&*8Rc`U72yHcqK-2Wg=;`rY2P}$d2!*G zO`mR&M{WL^wyMUY629K~;%h@+5qhAg=6T<|YKKk0W0vt!x78jSxRm$2(L;|Dvz`R$ z$Iv}DM6{enDe-qA$-@7quCI=Z>g(1Y14SAfkXAZ|ZV;4|mQse6?v{~K8YHAkN*JUB z85ocn1f{!Mq?GQEQi0zdzVE&7d++D|{?X5P&hhNC_u6Z(^{lm?MP(1)xb4-P6cN(Q z^wd};l2;7~a~GPUjdAuJ>tOj1Qga!@X#L-sv*y*mHK(Otn1?Y)jgZ;b0O2+u$CQnT z%zuRne`Kf}8)ml-e0{_&>_im&hOz6$v{DgFvJdDg@6cHp!sXj325?P*r8tnd5&9_r zwTqUAk4kiRTE%Ce?xpeOUK*>YlCoGRK0cZY%6Z$^ERXAr^QZgZn7&^Bmw#gl!!>;9 zr@8CPEx%O3t{Qn7$;!l&!cu=7*} z$SLmc4F4*_FiIb0Sx5^|co1B_*VfE-*H~+Vy{?DR-?$0E8DIXF({}%{h|0g!T>;L& z;S*p0_5<^^mg|Yd5z5C#mW?67S`+u0?G1)FLPG2AB&KlN84R0s-|xH4c)mxU<;j+E zxP=&$*+FTPBU3IaXVZv63C*`ADgz825~OB=Oj4UwAQXR2sV`+TED%-nFEO-B0#V=? zWDH^8>a~Dkn*zI9XY$xp|^VS31LP%-2(^y%k!+Mc`<_ zb1q3bb(L3g30Awa6lBWn2%1bnw|VT7vlka$g;{B6w^mX^7_LAm5T?a^~wnqI>px0u9#CGf-%dfy%=*cg}McNkwo6S9JEXLBY3 zC|t*u1Zb22FGZNF6%xjN?b6My5<-MlC z+O30?8&i!)g+QQo|A(46APyxvV*EtW_cZ9zr|9e*B)~Ll9*uugj?OEFo!G1A!zS5o z4S1G>vQeGeMU*H+YT7>r72WaZp4=P`3WNP2^2ivKN6 It1Jl?=V?~VRF>=_{X6V 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 literal 0 HcmV?d00001 diff --git a/doc/source/conf.py b/doc/source/conf.py index 0e2ef576..8621574a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -161,6 +161,10 @@ # Sphinx gallery customization +nbsphinx_thumbnails = { + "examples/minimal_example/api_aedt": "_static/thumbnails/coaxial.png", +} + nbsphinx_custom_formats = { ".py": ["jupytext.reads", {"fmt": ""}], } diff --git a/doc/source/examples/minimal_example/api_aedt_example.py b/doc/source/examples/minimal_example/api_aedt.py similarity index 67% rename from doc/source/examples/minimal_example/api_aedt_example.py rename to doc/source/examples/minimal_example/api_aedt.py index fca2cacb..3b803c0b 100644 --- a/doc/source/examples/minimal_example/api_aedt_example.py +++ b/doc/source/examples/minimal_example/api_aedt.py @@ -1,14 +1,15 @@ # # AEDT API example # -# This example shows how to use the common AEDT API to launch a new AEDT session in a thread, create an Icepak design -# and create a box. +# This example shows how to use the common AEDT API to +# launch a new AEDT session in a thread, +# create an HFSS design and create a coaxial. # ## Perform required imports # # Perform required imports. # + - +import sys from ansys.aedt.toolkits.common.backend.api import AEDTCommon # - @@ -27,12 +28,11 @@ # ## Set properties # -# Modify properties with a dictionary. +# Set non graphical mode. -new_properties = {"use_grpc": True, "debug": False} +new_properties = {"non_graphical": True} flag_set_properties, msg_set_properties = toolkit.set_properties(new_properties) - # ## Initialize AEDT # # Launch a new AEDT session in a thread. @@ -43,13 +43,16 @@ # # Wait for the toolkit thread to be idle and ready to accept a new task. -toolkit.wait_to_be_idle() +idle = toolkit.wait_to_be_idle() +if not idle: + print("AEDT not initialized.") + sys.exit() # ## Connect design # # Connect or create a new design. -toolkit.connect_design("Icepak") +toolkit.connect_design("HFSS") # ## Get toolkit properties # @@ -59,12 +62,12 @@ # ## Create a box # -# Create a box in the design. +# Create a coaxial in the design. -box = toolkit.aedtapp.modeler.create_box([10, 10, 10], [20, 20, 20]) +coax = toolkit.aedtapp.modeler.create_coaxial([0, 0, 0], 1) # ## Save and release AEDT # # Save and release AEDT. -toolkit.release_aedt(True, True) +toolkit.release_aedt(True, True) \ No newline at end of file diff --git a/src/ansys/aedt/toolkits/common/backend/common_properties.json b/src/ansys/aedt/toolkits/common/backend/common_properties.json index a1a3f75a..1d38e17a 100644 --- a/src/ansys/aedt/toolkits/common/backend/common_properties.json +++ b/src/ansys/aedt/toolkits/common/backend/common_properties.json @@ -10,7 +10,7 @@ "is_toolkit_busy" : false, "url" : "127.0.0.1", "port" : 5001, - "debug" : true, + "debug" : false, "toolkit_name": "common", "log_file" : "common_backend.log" } \ No newline at end of file From c2e07ed77e6f8cc8f2f82b716d99ab096fba4f10 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 11:57:00 +0100 Subject: [PATCH 05/20] Add example --- doc/source/Getting_Started/Installation.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/source/Getting_Started/Installation.rst b/doc/source/Getting_Started/Installation.rst index 11e44375..9513d0c4 100644 --- a/doc/source/Getting_Started/Installation.rst +++ b/doc/source/Getting_Started/Installation.rst @@ -1,2 +1,15 @@ +.. _installation: + Installation ============ + +If you want to use this library for your toolkit or automated workflow, you can install the backend standalone or +install backend and user interface common methods. + +You can install both backend and user interface methods from PyPI:: + + pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git[all] + +You can install only the backend methods from PyPI, this is useful if you only need the common API:: + + pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git From 301983631eea3ba50a0997910c769828cd091b17 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 12:04:05 +0100 Subject: [PATCH 06/20] Add example --- doc/source/Getting_Started/Installation.rst | 15 --------------- doc/source/examples/minimal_example/api_aedt.py | 5 ++--- 2 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 doc/source/Getting_Started/Installation.rst diff --git a/doc/source/Getting_Started/Installation.rst b/doc/source/Getting_Started/Installation.rst deleted file mode 100644 index 9513d0c4..00000000 --- a/doc/source/Getting_Started/Installation.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _installation: - -Installation -============ - -If you want to use this library for your toolkit or automated workflow, you can install the backend standalone or -install backend and user interface common methods. - -You can install both backend and user interface methods from PyPI:: - - pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git[all] - -You can install only the backend methods from PyPI, this is useful if you only need the common API:: - - pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git diff --git a/doc/source/examples/minimal_example/api_aedt.py b/doc/source/examples/minimal_example/api_aedt.py index 3b803c0b..b1208fd3 100644 --- a/doc/source/examples/minimal_example/api_aedt.py +++ b/doc/source/examples/minimal_example/api_aedt.py @@ -1,8 +1,8 @@ # # AEDT API example # -# This example shows how to use the common AEDT API to +# This example shows how to use the Common AEDT API to # launch a new AEDT session in a thread, -# create an HFSS design and create a coaxial. +# create a HFSS design and create a coaxial. # ## Perform required imports # @@ -11,7 +11,6 @@ # + import sys from ansys.aedt.toolkits.common.backend.api import AEDTCommon - # - # ## Initialize toolkit From 3b5a46f9d6c100e40e124e4565cba59999026e48 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 12:05:40 +0100 Subject: [PATCH 07/20] Add example --- doc/source/Getting_Started/installation.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 doc/source/Getting_Started/installation.rst diff --git a/doc/source/Getting_Started/installation.rst b/doc/source/Getting_Started/installation.rst new file mode 100644 index 00000000..5a0d3d17 --- /dev/null +++ b/doc/source/Getting_Started/installation.rst @@ -0,0 +1,15 @@ +.. _installation: + +Installation +============ + +If you want to use this library for a toolkit or automated workflow, you can install the backend standalone or +install backend and user interface common methods. + +You can install both backend and user interface methods from PyPI:: + + pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git[all] + +You can install only the backend methods from PyPI, this is useful if you only need the common API:: + + pip -m install git+https://github.com/ansys-internal/pyaedt-toolkits-common.git From 6bbde5f8cd2c1052e9de3fa16bbc2bd83c338941 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 12:14:44 +0100 Subject: [PATCH 08/20] Add AEDT Common example --- .../examples/minimal_example/api_aedt.py | 2 - examples/backend/api_aedt_example.py | 59 ------------------- src/ansys/aedt/toolkits/common/backend/api.py | 3 + 3 files changed, 3 insertions(+), 61 deletions(-) delete mode 100644 examples/backend/api_aedt_example.py diff --git a/doc/source/examples/minimal_example/api_aedt.py b/doc/source/examples/minimal_example/api_aedt.py index b1208fd3..b9fc5883 100644 --- a/doc/source/examples/minimal_example/api_aedt.py +++ b/doc/source/examples/minimal_example/api_aedt.py @@ -8,10 +8,8 @@ # # Perform required imports. -# + import sys from ansys.aedt.toolkits.common.backend.api import AEDTCommon -# - # ## Initialize toolkit # diff --git a/examples/backend/api_aedt_example.py b/examples/backend/api_aedt_example.py deleted file mode 100644 index 288a186c..00000000 --- a/examples/backend/api_aedt_example.py +++ /dev/null @@ -1,59 +0,0 @@ -from models import properties - -from ansys.aedt.toolkits.common.backend.api import AEDTCommon - -# Object with generic methods to control the toolkits -toolkit = AEDTCommon(properties) - -# Get default properties -properties_from_backend = toolkit.get_properties() - -# Set properties, useful to set more than one property -new_properties = {"use_grpc": True, "debug": False} -flag1, msg1 = toolkit.set_properties(new_properties) - -# Get new properties -new_properties1 = toolkit.get_properties() - -# You can set properties directly -properties.debug = True - -# Get new properties -new_properties2 = toolkit.get_properties() - -assert new_properties1["debug"] != new_properties2["debug"] == properties.debug - -# Property type can not change -properties.debug = 1 - -# Property type can not change -new_properties3 = {"debug": [False]} -flag2, msg2 = toolkit.set_properties(new_properties3) - -# Get AEDT sessions -sessions = toolkit.aedt_sessions() - -# Get AEDT installed versions -versions = toolkit.installed_aedt_version() - -# Launch AEDT. This is launched in a thread. -msg3 = toolkit.launch_thread(toolkit.launch_aedt) - -# Wait for the toolkit thread to be idle and ready to accept new task. -toolkit.wait_to_be_idle() - -# Get new properties. Now the properties should contain the project information. -new_properties4 = toolkit.get_properties() - -# Connect to the design -flag2 = toolkit.connect_design() - -# Get new properties. Now the properties should contain the design information. -properties_aedt = toolkit.get_properties() - -# Create a box -box = toolkit.aedtapp.modeler.create_box([10, 10, 10], [20, 20, 20]) -box_name = box.name - -# Release aedt -flag3 = toolkit.release_aedt() diff --git a/src/ansys/aedt/toolkits/common/backend/api.py b/src/ansys/aedt/toolkits/common/backend/api.py index a4e34583..28bb4bb3 100644 --- a/src/ansys/aedt/toolkits/common/backend/api.py +++ b/src/ansys/aedt/toolkits/common/backend/api.py @@ -355,6 +355,7 @@ def launch_aedt(self) -> bool: if not connected: logger.debug("Launching AEDT.") pyaedt.settings.use_grpc_api = self.properties.use_grpc + pyaedt.settings.enable_logger = self.properties.debug desktop_args = { "specified_version": self.properties.aedt_version, "non_graphical": self.properties.non_graphical, @@ -424,6 +425,7 @@ def connect_aedt(self) -> bool: # Connect to AEDT pyaedt.settings.use_grpc_api = self.properties.use_grpc + pyaedt.settings.enable_logger = self.properties.debug logger.debug("Connecting AEDT.") desktop_args = { @@ -496,6 +498,7 @@ def connect_design(self, app_name: Optional[str] = None): design_name = self.properties.active_design pyaedt.settings.use_grpc_api = self.properties.use_grpc + pyaedt.settings.enable_logger = self.properties.debug if not app_name: app_name = "HFSS" From 4ecadf73cfb7cd060e76f939d3c23aebb8314a2c Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 15:12:33 +0100 Subject: [PATCH 09/20] Add properties Common example --- doc/source/conf.py | 12 ++- .../api_aedt.py | 6 +- doc/source/examples/edb_common/api_edb.py | 83 +++++++++++++++++++ doc/source/examples/index.rst | 4 +- .../properties_common/api_properties.py | 54 ++++++++++++ .../properties_common/backend_properties.json | 6 ++ .../examples/properties_common/models.py | 40 +++++++++ examples/backend/api_edb_example.py | 49 ----------- src/ansys/aedt/toolkits/common/backend/api.py | 4 +- src/ansys/aedt/toolkits/common/utils.py | 35 ++++++++ 10 files changed, 237 insertions(+), 56 deletions(-) rename doc/source/examples/{minimal_example => aedt_common}/api_aedt.py (92%) create mode 100644 doc/source/examples/edb_common/api_edb.py create mode 100644 doc/source/examples/properties_common/api_properties.py create mode 100644 doc/source/examples/properties_common/backend_properties.json create mode 100644 doc/source/examples/properties_common/models.py delete mode 100644 examples/backend/api_edb_example.py create mode 100644 src/ansys/aedt/toolkits/common/utils.py diff --git a/doc/source/conf.py b/doc/source/conf.py index 8621574a..f7f28b23 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -162,14 +162,22 @@ # Sphinx gallery customization nbsphinx_thumbnails = { - "examples/minimal_example/api_aedt": "_static/thumbnails/coaxial.png", + "examples/aedt_common/api_aedt": "_static/thumbnails/coaxial.png", } nbsphinx_custom_formats = { ".py": ["jupytext.reads", {"fmt": ""}], } -exclude_patterns = ["_build", "sphinx_boogergreen_theme_1", "Thumbs.db", ".DS_Store", "*.txt", "conf.py"] +exclude_patterns = [ + "_build", + "sphinx_boogergreen_theme_1", + "Thumbs.db", + ".DS_Store", + "*.txt", + "conf.py", + "examples/properties_common/models.py", +] # if os.name != "posix": # extensions.append("sphinx_gallery.gen_gallery") diff --git a/doc/source/examples/minimal_example/api_aedt.py b/doc/source/examples/aedt_common/api_aedt.py similarity index 92% rename from doc/source/examples/minimal_example/api_aedt.py rename to doc/source/examples/aedt_common/api_aedt.py index b9fc5883..cbc61b53 100644 --- a/doc/source/examples/minimal_example/api_aedt.py +++ b/doc/source/examples/aedt_common/api_aedt.py @@ -27,8 +27,8 @@ # # Set non graphical mode. -new_properties = {"non_graphical": True} -flag_set_properties, msg_set_properties = toolkit.set_properties(new_properties) +set_properties = {"non_graphical": True} +flag_set_properties, msg_set_properties = toolkit.set_properties(set_properties) # ## Initialize AEDT # @@ -55,7 +55,7 @@ # # Properties contain the project information. -new_properties_from_backend = toolkit.get_properties() +new_properties = toolkit.get_properties() # ## Create a box # diff --git a/doc/source/examples/edb_common/api_edb.py b/doc/source/examples/edb_common/api_edb.py new file mode 100644 index 00000000..5efe0283 --- /dev/null +++ b/doc/source/examples/edb_common/api_edb.py @@ -0,0 +1,83 @@ +# # EDB API example +# +# This example shows how to use the Common EDB API to +# open an existing EDB project. + +# ## Perform required imports +# +# Perform required imports. + +import sys +import os +import shutil + +from pyaedt import generate_unique_folder_name + +from ansys.aedt.toolkits.common.utils import download_file +from ansys.aedt.toolkits.common.backend.api import EDBCommon + +# ## Initialize temporary folder and project settings +# +# Initialize a temporary folder to copy the input file into +# and specify project settings. + +# + +URL_BASE = "https://raw.githubusercontent.com/ansys/example-data/master/toolkits/common/" +EDB_PROJECT = "edb_edge_ports.aedb/edb.def" +URL = os.path.join(URL_BASE, EDB_PROJECT) + +temp_folder = os.path.join(generate_unique_folder_name()) + +edb_path = os.path.join(temp_folder, "edb_example.aedb") +os.makedirs(edb_path, exist_ok=True) +local_project = os.path.join(edb_path, "edb.def") + +download_file(URL, local_project) +# - + +# ## Initialize toolkit +# +# Initialize the toolkit. + +toolkit = EDBCommon() + +# ## Initialize EDB project +# +# Open EDB project. + +load_edb_msg = toolkit.load_edb(edb_path) + +# ## Get toolkit properties +# +# Properties contain the project information. + +new_properties = toolkit.get_properties() +edb_project = new_properties["active_project"] + +# ## Save project +# +# Copy current project in a different file. + +directory, old_file_name = os.path.split(edb_project) +new_path = os.path.join(directory, "new_edb.aedb") +toolkit.save_edb(new_path) + +# ## Get cell names +# +# Get cell names using PyEDB. + +toolkit.logger.info("Play with EDB") +cell_names = toolkit.edb.cell_names +toolkit.edb.nets.plot() + +# ## Save and release EDB +# +# Save and release EDB. + +toolkit.close_edb() + +# ## Remove temporary folder +# +# Remove the temporary folder. + +shutil.rmtree(temp_folder, ignore_errors=True) \ No newline at end of file diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index fc772b46..be86ee4e 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -4,4 +4,6 @@ End-to-end examples show how you can use the Common toolkit API. .. nbgallery:: - minimal_example/api_aedt.py + aedt_common/api_aedt.py + edb_common/api_edb.py + properties_common/api_properties.py diff --git a/doc/source/examples/properties_common/api_properties.py b/doc/source/examples/properties_common/api_properties.py new file mode 100644 index 00000000..a42afa03 --- /dev/null +++ b/doc/source/examples/properties_common/api_properties.py @@ -0,0 +1,54 @@ +# # Properties example +# +# This example shows how to use the properties models. +# These properties allow sharing information through all the workflow. + +# ## Add new properties +# +# Before importing the common module, you can add new properties. +# You first need to create a file which contains the new properties type, [Models](models.py). +# Then add a file which sets the needed default values, [New default properties](backend_properties.json). +# Finally, import the properties. + +from models import properties + +# ## Perform required imports +# +# Perform required imports. + +import sys +from ansys.aedt.toolkits.common.backend.api import Common + +# ## Initialize toolkit +# +# Initialize the toolkit with the new properties. + +toolkit = Common(properties) + +# ## Get properties +# +# Get properties. + +toolkit.get_properties() + +# ## Set property with set_properties +# +# Set new property. + +set_properties = {"invented_property": [1, 2, 3]} +toolkit.set_properties(set_properties) +toolkit.get_properties() + +# ## Set property directly +# +# Set property directly. + +properties.invented_property = [10, 20, 30] +toolkit.get_properties() + +# ## Set wrong property +# +# It is not possible to change property type. + +set_properties = {"invented_property": 1} +toolkit.set_properties(set_properties) diff --git a/doc/source/examples/properties_common/backend_properties.json b/doc/source/examples/properties_common/backend_properties.json new file mode 100644 index 00000000..3c3ac7e2 --- /dev/null +++ b/doc/source/examples/properties_common/backend_properties.json @@ -0,0 +1,6 @@ +{ + "non_graphical" : false, + "log_file" : "example_backend.log", + "invented_property" : [10], + "test" : "hola" +} \ No newline at end of file diff --git a/doc/source/examples/properties_common/models.py b/doc/source/examples/properties_common/models.py new file mode 100644 index 00000000..f20beff3 --- /dev/null +++ b/doc/source/examples/properties_common/models.py @@ -0,0 +1,40 @@ +import json +import os +from typing import List + +from pydantic import BaseModel +from pydantic import Field + +from ansys.aedt.toolkits.common.backend.models import CommonProperties +from ansys.aedt.toolkits.common.backend.models import common_properties + + +class BackendProperties(BaseModel): + """Store toolkit properties.""" + + invented_property: List[int] = Field(default_factory=list) + test: str = "hola" + + +class Properties(BackendProperties, CommonProperties, validate_assignment=True): + """Store all properties.""" + + +backend_properties = {} +if os.path.expanduser(os.path.join(os.path.dirname(__file__), "backend_properties.json")): + with open(os.path.join(os.path.dirname(__file__), "backend_properties.json")) as file_handler: + backend_properties = json.load(file_handler) + +toolkit_property = {} +if backend_properties: + for backend_key in backend_properties: + if hasattr(common_properties, backend_key): + setattr(common_properties, backend_key, backend_properties[backend_key]) + else: + toolkit_property[backend_key] = backend_properties[backend_key] + +new_common_properties = {} +for common_key in common_properties: + new_common_properties[common_key[0]] = common_key[1] + +properties = Properties(**toolkit_property, **new_common_properties) diff --git a/examples/backend/api_edb_example.py b/examples/backend/api_edb_example.py deleted file mode 100644 index 4c57d11d..00000000 --- a/examples/backend/api_edb_example.py +++ /dev/null @@ -1,49 +0,0 @@ -import datetime -import os -import shutil -import tempfile - -from ansys.aedt.toolkits.common.backend.api import EDBCommon - -# If you want to add backend_properties.json to the Properties, you need to import: -# from models import properties -# And pass it to EDBCommon() - - -local_path = os.path.dirname(os.path.realpath(__file__)) -test_folder = "common_toolkit_example_" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") -scratch_path = os.path.join(tempfile.gettempdir(), test_folder) -if not os.path.exists(scratch_path): - try: - os.makedirs(scratch_path) - except: - pass -example_project = shutil.copytree( - os.path.join(local_path, "example_models", "edb_test.aedb"), os.path.join(scratch_path, "edb_test.aedb") -) - -# Object with generic methods to control the toolkits -toolkit = EDBCommon() - -# Load EDB project -msg2 = toolkit.load_edb(example_project) - -# Get new properties -new_properties = toolkit.get_properties() - -edb_project = new_properties["active_project"] - -# Save project -directory, old_file_name = os.path.split(edb_project) -new_path = os.path.join(directory, "new_edb.aedb") -toolkit.save_edb(new_path) - -# Save active project -toolkit.save_edb() - -# Edb API -toolkit.logger.info("Play with EDB") -cell_names = toolkit.edb.cell_names - -# Close EDB project -msg2 = toolkit.close_edb() diff --git a/src/ansys/aedt/toolkits/common/backend/api.py b/src/ansys/aedt/toolkits/common/backend/api.py index 28bb4bb3..7e06159a 100644 --- a/src/ansys/aedt/toolkits/common/backend/api.py +++ b/src/ansys/aedt/toolkits/common/backend/api.py @@ -836,9 +836,11 @@ def load_edb(self, edb_path=None): if self.edb: logger.error(f"Close EDB {edb_path} before loading a new project.") return False - print(self.properties) + if os.path.exists(edb_path): aedt_version = self.properties.aedt_version + pyaedt.settings.enable_logger = self.properties.debug + pyaedt.settings.enable_debug_edb_logger = self.properties.debug self.properties.active_project = edb_path self.edb = pyaedt.Edb(edbversion=aedt_version, edbpath=edb_path) logger.debug("Project {} opened".format(edb_path)) diff --git a/src/ansys/aedt/toolkits/common/utils.py b/src/ansys/aedt/toolkits/common/utils.py new file mode 100644 index 00000000..2b5353e1 --- /dev/null +++ b/src/ansys/aedt/toolkits/common/utils.py @@ -0,0 +1,35 @@ +# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utils module""" + +import requests + + +def download_file(url, local_filename): + """Download a file from a URL into a local file.""" + with requests.get(url, stream=True) as r: + r.raise_for_status() + with open(local_filename, "wb") as f: + for chunk in r.iter_content(chunk_size=4096): + f.write(chunk) + return local_filename From 540e5d2f76c2d6ecbfa3a4419ca50cf78a21ae41 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 15:18:05 +0100 Subject: [PATCH 10/20] Add properties Common example --- doc/source/_static/thumbnails/book.png | Bin 0 -> 12502 bytes doc/source/conf.py | 1 + 2 files changed, 1 insertion(+) create mode 100644 doc/source/_static/thumbnails/book.png diff --git a/doc/source/_static/thumbnails/book.png b/doc/source/_static/thumbnails/book.png new file mode 100644 index 0000000000000000000000000000000000000000..d27305b890bc61526467f64e51f5409bdfa44d09 GIT binary patch literal 12502 zcmZv?cQl;e_Xaw8@4W{>1S8SAAbJaeV4?(x61<4sX9yvLL??(4qDG(SL-ZheFc_kX z7QKY~`nW6WyMFhcKh8R5?`QA*oW1usUW-=`^wi0SnTa6~2$`nFeM1NY8-PHtItXz< zi7J;^F9d=Ed7$%9RUOdr{crgA_<$;K26y1>?d=WD;JDU?hK7P`PzY|p^)&{Zjg5`} zL;b&2*K$yDjlWi1&+hK-pz!I_r!QW-xQ6`;;o{=r=jZpoU=Z+6`+wx%7Sw{{dhyTO zKNzUGw!Ow&!~TE0d3t*Olm7$%^8>OC2nhJMy+(p2pafh40C26kmj8dx3u?W*yuedm z<3a!6_J3iZ1RU2+{@2wt9&`)h|Bw7%mac<>+W+I>8vk$phrgD9;D3hT;@b9qJzQ%+ z$@KuYAcy}o0oT{#ni~iMJ%dp|p})WXbvUph{|JBpPz7$Ut*-IcB?Bd(6EMTqfNRV( z09;=iUR!}P==@rE9qU^DFXFWdoI!`z96-Ro(AQK!2?)3zAO_Te1KfIB)xp5NzkFe+ zX9T(bv;pfoS5ef(Ums)a<1|rRD$6RI^O@Y!(6a;lldZL^eIi0U60=E*jUxh@oxQP5 zhi`5LFge{nPYz8Jmh9*&D7GK1gaeHjf0I1EpUdwTGwnI7;>^`zdw&g1k+bdoo?e)s z?R!ia*kj{J>^#>r|FP>1wQmiaoAi~w`jBII(6(iNxe|OV$bAPUaj(mEE5rhQ4%VWO>c5nd14S`kh~h(nE$saoOmg2TWSe(vv(CZ)vN#iwWtxd7k>hh4<#Y zsX-kq2;>Gt^S;tU->Kj4ePFbj^j*&QxG!zu1#?3~3vbOXzjAeND|4DOTBO$RWl0kBP>bw;biAk$0*@iS20VtL$BZ2N_yeEd4pYpY43^ zo*=2K7JXJ)XrDRshCA}CYg$_~IbOVp#GH(TM@n?hg>B$a_fN8@lf(w%_ES=CjLz`X zUge7ZEMu&nr!y>B#zSe~p0c7zfyMmrbdz0V`tIdv=hKOicb}lm5mZ6iEY`RBqGmeW zL=PgS3g~A)^u=L$gbC%lsCpaybuMBK5c1@OVk6x~(ABtPq+F0ypV0QMfs&z%mHfnuv`y&iBAmLz$LWB$EdJ*w@YwB#G@XVgBnZvgjht>@h#{LE&VZsldV zQ9Ux{4}2H&Ey@>Ebno+Q{1C`>fNiv#kZ>xH%#3Dm6>RlnvZ?~O{@>{FoC5^+QTplb1*`ggt{NC|9=}r)z3M*(u%`DUXMmZ+l3G`yC z5SY?zWsK&*C6^eYe|P7dX%de6k3BvVuFP*`j7`6=T~t=>X>D3+p=LF`7B@FGixJa% zYOkC3Ut}*IM@_mG-Qmiod1Tg*@AIub9pJKvNJ>m-TOCETeuuGUzoL|UHf@U}l71Lp z(Xw&_TUJz9hhaK5M+RS!iAaDmuU_BUF&K+0J-y-AVsw6#WH{vp2ks|{K5cGtv`Bq} z_3e2n&c)ky;^wRkTc_o{Um}SkxCYm0`iugrr|Hc%K0mZHC{9UFvmYy2dn#l5EY>_= z8Yq}}X~gB*cd(A?XLb8UHp!L?&6x2WISXINPa3;TLqo&Cl2LSL_f%qANa@Vrt;qs} zMA$ypztU_eoLtt^iENm2q0N}^uf(0byDt)dX~w`P*w`IhY~OCoNr=>rnbt>|t(;p} zqmyUGPBtZ)&r@<{&R#rn{DXXFK>Z_lF?van!hWifgmS~_P5sPZ;Lcd|p&w_a!a2I3 zTNmN=yKjb?vaW;SMS#_%*Iu_a`u2L0TkJ$Pdv}8;bhmtxJMkyHTFd^`>{Ftk<_yOI z?VryIsKbB^iyyC>CdmJmOTM1Sb|c!ppra!=TBv|*nD3X@AR{OY`;J$A`COmL|1c6l z-h#LuZknm@%CJgJzj<>!I?Z)v@%q z7Vq9S=t0l%Wlei{|=x-MOrY?(0_L*esf^yH5vd_$W4=gk?LcgJzb} zF#i4t{`6ff+(Gw=Gsg$&>>8K&b?3rX$A!}LB1nm#bcj@no(#VWHZx93Om*$DQ2V}h zGR1zq^de7tOX!9{#q&!)C57>YxrDx4d~(P(#OSH~KGNbepy@MHscqUBzj}-DUc7vmUmp^Ws;P{sf~@IRr$1Cwx#-Bn?m)V{1K-GvaC|p7M;VQhnW?QU zB$YN}NsZUelDoI;ibG|LIwgO^89Z=-(p5Q(<6~YhRa&aTMD&OL_J5(@H1PhqkW5-g z+Pe0HXO7$E2W&#Y7<k;ecWmkyavy6IL`x-~=jbkeDw%-<;f-Hj7*t%q`?$#>|T$GX- zGf$eAWwa)LW{!znw` zUbPW$q}_XsB~|vEE^s~f?ugK1&UVap|AEvi+)kFzU#`(N3DeE$pjCp*_CLgky#Cyk zEwjtQk|N(6qGh}RSdmm~sR@-z=<@d{S6gLt~d^5|PZ? zOq*;Uup&Y_hDBRZ{0s5dZ>pPdm@9*K3twFm<;i$ko08a!qNi`|&Ir4a8DUe<8vt7_Yv#diO|{nc^L9KZOx#$LAtRS(59 zu(od$R~6{qF~&beJt5#&EpY1ea}6VO4oCJnokOU}8nd(N>i2yZ^g4_gztA;%ETnXw z3~NS@$W=p3o_pch#3z-Jh#k$scTB8bmqXt_2=-1Pr)SNgl4%_$JlVM$7=gFJS;ZW5 zYa6Yrn6eTdRdL&zZ9$mBd?fqbisQMnh^BF9Rdox+Q+3vG$-XkF80=3um&eWP9rX&O zrV8yPv5%Yswpb)eZ|BLA1x45Jh8mbW>045j*=H9cHtH)cFYqh3*NAmj^n|vg{JLPO`zI7SlSg!QrE$lAX@|&-cEksJ)6jB(n6O}Fz zXMKvB7$~7b2R>rEoe|H*@7#qEHT-2_Vk&{iU3I^))H$??-C9VNvW+BOKUw3Iiymbq z9=XF-E)Bu_rg_l$62#!SL19%8f!2vhoDAD9eyoVF^ycVtgEKepS;GgmaB{Igmkpv5 zAA)15dZu_PkdKZ$59iB|dUD~iu{qGGuPYMpJCK5!yUT=E=ZvjPc}cIhrg7f^w?nzu zXV({09?d>y%c9!m*J0rSd_IaDI6=f!93T_bK#{k47&>LNFrSS3c9|ihmZz^RUep>V?`o|2r5c z=%Fj$20^FL_n3#LQ!kLo%@fDgTXJY+_I*0~^i~0|CepU&bI8k6)*Y}^Tjub9okyN5 z_RciYi9pC-+vRU~J8QnW=N>(mI4{d|B#oX;=>}#-|3-3cMumK04aMmZ^aU@i5Lw(? z(<*GpJG+-Oj9)*?x zesRI=@lmheRN?=D#I0kTwKAkkoroWN6WKr8snNZQps(~-nb-Ul^H`@KWSFToi3sSN zNsIlfuAvJl`q-jKB>31kOy>8#diGCQ@1?;rZsuNGDQkIzCZB!9;yMMq$M($yZJM>B zd=WHSVXzpgmb&lqr06*C=~^u;6o=ihyK%EW-ts_+-qF}Oi1Qs@o8|DAB7qOmEY8)d z$N4M`b~vo5;q`SN(~@wVDJi%xg92!F`VIhR)??YCcNn@`o19(RR438tO6~F#eu;1W8^Jo4VImeeHCe zhbL1cTLxWuDJ?mcG-x3{%sq?nue}??B{Dzt&8Jnwd>UaxscIkBXy?ZI-V}J+#+HQQ zNPcMas!Vnm6VND@MI5pZj(RXuHn8%qNRn5-d=8PmC5(A(b2jL#`_`eKpWcGlrtTy@ zzyH=m@ZFas$l?c*mc1lWcdT#y{Wj$O*@X36*?eC2$60qJ^f3-?@y@3o3tIO0tWlqI z=UHw}`Xj3H=&~r9-uxykoP0PU2w>hW;FJ~2dluufgL^Ax8;+$-XC+5c+h<$b zEPoc42~rqJJ5l0upEvulN1*BO1Z6xZ@dAechj<&4d}38QOeb|FAI863e0$>X2Tg>I zmN*SuKo7_YZU1#o*f9VoLqFyO_Ayg5AZjN(X?TiV&MM86jRx+l27c}OTx=-G2gDYnjQh9auv zeZ4=#GDGovgi$A0Teo4Jr$>*)Kp5ZjgnUe6c7UCnfgdUulm6Gc`kEVrbUTq7&bXfXw+nJPOA(L@e2#|(AzbQy>YNP)2 z9&s5X>V$ObbuEE@z~cw9`ItX6nS&UG@k7jf#zE0bT66~PiYSr+gdabBU&MuHnuIxr z9Ff5Gp14qCMI4)mC&G($L7uX?n<&!}D}z^tfY%kzHB9=TbHI-7yz#mpoLdSDnKs29 zXmnh;eiLsZ9TSLO_i%bT)~Q`Zq1qX;We?JXnYmCjs~@R$hYUbqUhq8}w5Kw#lkjsf zzCjQlQ%?o5((Apb7h?H?U4ey6xMI~)Ir}|cR2m=SLBont?K63J=GJ}J6bIP5B5*v| zyEet%c)6?Dy-0`y=n@S)1FXQzk#DncR=Jb5(s4?`W0Jj((?T+7VM1U$*7LeYcBQqZ zv@K&VG>Er&u2KiM2xGO>eN`dea)xsw9r)kO>F6iJ1(d+@1+bQ#gx99vWjDP_fwO3V zDQGC!-27HRa;Aj}QUP!ogm!$#597E0GKaEgfcSRdFMn00SwfxERho-kAg=Ua+Fg@s zj~I)_T2+#CTE+k4bD3jKCnqt0i>AB^Pw%o#@X)!CSp#K%6KpH8Xv{Cu`qrxskQ1OuuDwvv zW8;H7Ofn@{iE-xuz6UOKtmw!qb8oOvvPokPvHmNx{nRY|!-ks#M^smh1+SIFL9p=P zO}q#DCgm1x9r@T{u{f?efDH^f1-!e1T>5W4QPX5!=&iey01tQx0txhgsH-^y3_bzm z!PvvNXu^luV18)A8a`|usMORw)a6I+U$sE+)U4XW7$YMhkm08fl&~ODR|ynVCImfK z-LzadZjl5!e1Fw$2Gi3qPLZPyU>OTFDdDoQPMTm~E`X+{$(9lA%a8QAI`u(KQ$=Ad zw;cuO?;`uQGSFdAieTo(JHo#@OMr{8EPh=ShENYu_O;fOXe`)}yr()l4uH!>88czrz3XYl=16%q;~U!OeCo%oA=VL^bi@ zMb4pLqg4URQ0Gs=&7@AT%Ae-IdkZ?2eBey^Cx`+E@FWCl(7xY+&N*zDvSUIca98k< zrUxA+lxXVz3KtI-z;+s0#7JB{Qp6iE!eLW1oZ0d`U~`j~#WX*{9_0-W{AorG6$WoZ z{0B-jEk6?Ps!!mj3-=z#7A{k-kA@*moT=2r+ouLH` zhfICoc}Blw!tU!Ek0BhuMh@M@l6(y+G?;#Dd~gCYluw47%rT`Q54|A;uY#2EuId`= z=3{!VI?*GCXU^rpZ!A+{-fc1ubdPVnTN69`G1^;eIE@iz7IZx zdE~f~U_Q6TQ8UgDN(lUa%@;ay165a#^q@AXH2h1E1^z_V6#slyXo~x(vEj$C_pHYg~PzBSD z=ZC$&TsQ@qIeJE9;QK3@6PRXV0wF#vN)=eRreh*V2~elR95!pT4!$JmzI$C)H8dmh zM;Hh9VQy1r+OFNi^C8~%C>j(*$f{i(LJ`;Wy9iPqcLfJog5TkiXkiqMCjww=$wBezDB*q2m|&h@MQ#KsZ3{>v<$U)xXjnt?@m~=% zNtRg66Ar`(pcToYBW#t}b&!P}!D>F3>k+$R zS{OC%1Q?a&ijW6>hYDCk_*D*BSLJQl&|X*J+?0TY$fLg9Wh=0dBpyP%m}II{5*_I7cQC}T)xl8#E(*xA}myaxcp*T zv@m6+Z)*Ro#*-AHfeKg>Jb-*(!i>NNj$tLKZ=mwKf4s8EdlHDG1lmD;VlsT1nFesa zg1&{TtdOJ>5*q?|JInffQe7T;#N=cI!MfzGv{#=@>AJvSIq(jI=H>8Q_T{AFSkAAT z$;t##(#f-iz~;SegAGhFSXs?Vk}cIf%+sqP-@r>@1i$=1;~Xl^1bSt_ zXc4oH=~q++;+DS9!VCz@YV3h*%1ql=ElnsPUa2k-J>oLX)&6%Oa5-Ew8}7;@`q^0w z6m;dsEOcUnc!IrpkccRbl)S_PbI|Y`UhK+*?gC-8nkZweCgDO07{zvc)`|{6L|#wuv+N9djdfq-*t9U{a6igOY2 zdnVwK?ixG6j?Y|mmCs?Ew`5{s_=C?kb75TfQ#qH+U%&(&LN&>_MRjhbDD!?CO8m~L zQ`YrYLhZh2(J$}#U;Z!(Pg2kE$ymRvUs5ffe3}9_n>M|Awi3}9EE z7~@M1INc!p)k^&ob%N>$5dtBtO-v?C>}XHq@f%z~j^wx`w~3M96QnCBLV*YGhz)jb zk1dsDPqF63!wN%cl5UA2k8$t-IchTw+K%YL(*B%>cDOl9&oNjzuUG2P@=I70mZhCZNAW7rdqGu2DEMH(KnID4XYA` zeoqiF>v9Pi`}aOStfh+SFse~Z;-6CdS#oi3L5;;4i<~=!3Dw^jv-pt8ozf>+_^_yN zD*MkKv1-X9KmEjySr*bSx3=YoCHy!zkwmw$X){S+=iCSEKW}e5LAuRp>pgwj=E0&a zTa@);b&EGf-;=m(^>3b-0fc~O*Ei{sEXg>SgqWL5-oD^MR5lVv=^TxE5w%;9tvDWz;@R3K&sgZOR^43i zW^1-InL59;T)VqFLREB+eG$`&g*uW98Plq=8gwvL=+4@AgJz0|1L-wQgZAp{#d*3j zWjD{}(vD|0!dR>g2k#8*W3^*2`lRFZHN%0XW!VcFOgCK+(c8!jLgcG*xTLq(eFOPt z(mY9Q*`{adJ9cN_hq~)3%@e?V=j+q^j2cq`j&%>VCQ|1xVnk=pXGkBL4jMS(?Oc9E zxc3e+;!8$9F=p;v@F$v^%LiGvsc@m9GS)0PSO?vXz~KA z1|>k`36R21-&OB0MGQq0m&E_9KsL^3qEy!)WvNemN<9Cvc+$YEe=B=>Pk3YLi7tzh zmWg+qN6Wf)fSidVs)lkj4Z4~$DPl(z4ZZgEF* zr{y3!$bb)j=vnz4sr5XY@lmRjh^pt`=-d+@D&O4wu@s}Ww?uCUb8NrIa6K#7pCAh!Lrlgkh`^qcy5`(LbhnMRE#6qu=}(MU|QMTs*_ z3AgMCYf)O6JM4OPk&padIx`EVZ^+u7W&1)eal{#@F4+qQk(7DgDS#~Sw zhOGpd(b|?P_Y{_vp4|irYU4w^S;WK+N-VdTiUO0Xmquh#)H?Dpm!D>DAeGrrj*2X0 zXFIDewMQg%rk_VO$V_kyLLj96SN{uO?o<#ip|7BJIuBJq5n`q&_|7GV#gB50O4RuG^s$SQ#Pi^+C!e{(-EF;lzTWHyg%$Iy#F;^=x#=g*+H zY?B@dF`Ou}DBhFrCcYOXi2?E=*r%xx13P%IO_s3Lo2b*QLTW@92~Vo1OX9d47heEQ z4Hk-Smk_quaBn44q55pJX!r&ubJJ0HwQ_4wKB4$AmVyj#eO!a~*G8lAPo9vOhWF`{ zk`szjEmEgbCIFIS9WwF82UA^9Vm=mUxdJU55=DxeI(lS?2qjFv552&xzD*s>%u8{| zwaveE-|f57R+&0rc(+MdEC`!gY7VE6h!Fa|08_2`U?@F$=UB%LGyWs}(VcA{)LV*! z$W}6?$#cAdrteMCx+`2h1FFK9Jj)kQwl3WyQ!!^252wQ%`Se~9|V zpXI|JNy7Q+Mjv*a?K<3msS>X?j#%JUvIrS8sdLF?UY>-Vu9<)k8g+!BB(Cn7dn}|7 zV*qGBUC^>iPjF?VPV zq!l#|nhBe)v^7Z7&Am>3`*z&z(3j+DOuX278SfkJG$`i2r2;k)zy#d?>rTb2@I9bd zhk#W%*UJ}sDw+{e&tY;wlnaG&KQm0Bv?Fb<<@Ynif6Tt+q<#9;6_Ik2<_ z8@H~by4A7VgUVMxgLe&AkGrcOrK>?YL?DPASMtYbE9aVP3EAb0vKM56af97`Q0E!@ z1d|YUdL!#(I;)h>S65C(R#qQsglw27^AkxA>hj{(CU6XS#to_P%rl>FltTzg7}0>P zd{@?=#?D)e%$G`fyhP+5O0Y?3BLy{D#Zf;)0#5>GO&QyZ#8?QshO`Yhj%M=M0QgY2Z|j%bxF^(&oZw>@Y=T%2}|qF$ff-M3<*#-VbWu*1dU zAo9S;V_Cv2s>s%_CcIIJINzQ`EMzvCA4HhdFCKmCySTAi6Y?uME$xj~l3;d8A!VK3 z$x8av=ADp^<2)XcJfeXo;+-XqPdDdYDDL)#{8=|{;H|zf)leH_G4y>gy0S4_>^Yt! zYr53^a%o7J$az0!ww@^CjM2~C?>#!1>KU&sRY)=$m^!yhf)~63+*yq*^e0u9ng<>h zP1fCzhv1s_KLygQ{?bNr*FWggIr=5mdLSq=`^umGrTx}LrfPe4-3MvbeTroG2y3># z$S1ZAxYV*Wg5><$>az_@h09WZU5Rf-DL(44EhiFx2AaL5QNi2KZVhLVjS??l6|LU( zww)U~CB$WDg#vZw%XW47kLVl5A7N$(tS9}{8Fb2s#n`1BzPZSqrpaTJ|E8!Kl<2hE zFk&Ps_H5oIm~JWD8TdOIpagK6Vd+(wJZI6ZkX(MVfoCe3)v==ZQ)_4vDioL5K~a1y>LP^ex3pxAB@Zt_4rPl`9IDy)A^IAclhHM7zo_d zjhJ23Sa{fb_qEqwO3KVS1=LLX>58SYu!lgb$C*eqH`o3qfDW0Gy-fX7b}MeKHZfUC zDE~2k#I82(f*Y3UYx)o299%kKP7hMOoL-7370}SS_m1L9dp3V-5Qw4& zTp!2m(JlVnHB>loVeHlaj=)aoAO_m+f5KaA+BM@eCtI(Wgxmr)c5t4>Hntu@+rE91 zAJ*sDuosy$PFl}J$E_RR9wVLPdrOh`Rlx}7E3#ppfpKHxR_t2``_srlf5R;e=Z8h5 zl}9`$!-Uy4OniW#6rP5U=igAd$uu9x5-3BB*tL7@doEf#bx#F)=5-|p(y1!(OhSIb zypW!x1o!s(-+#HieM(dft9d*1$Eri~^lycr{9LNus7d z#WO8mg|qSb<`P-|d>uYj?cLp;>D|sI6A2;dNv7`JUaxT_wWP>?027SDL!*JoN54Cl zOC(vpd`Z&##cn2dtKda%k?hZ@wvwJ^SsR55GGu>|_bEa7FKg~tJjn_OL&^bkYwg=` z(C5t(zQxF-r})xzwI4bXO8AtZ)O4E}(Pt={;dIB~x1|84awtreZ+wAl^HfWhIi~85 zVUk)>m4_*<-tY0J5^5Vkcl2GVnPqfu-o~^ZaABKHG|?0rGgVr!4U}aVP+ru~aJ>{Z zF?%0Wl=I2&khp|sY;TzSf!<6BLd_zS3J*D^C2QEhJZ(wom z23dDL#GF#WlV zRBU<9jCx_uva+mQ`4S`#bRI^7-zO7<&%5DqH1%pXQ5pQJzy6~FCqX;8!Ju$?b?t) z&8<8Ss_c5Lh~5MVjz1@(NK2PQ?IFcAEb~FE%dUa;FvO=Gc_OsD^l3Wp?q-hw*4%yo z`CdP!qjBS>w;t=8ix1jz{tK5>Mjv`eAji`7~IJQc*0e|w$mzALx&!T8y|3wO4ngWs3UDjObn zkT0$HfM-Xc7ZZFxizjrhURSYP=Na&;uAjbwUK~d4T#s|Fi42otGvJf1picTVsBWEb zkZrC!dsV_xYE%GH+&?nh$t}Wgu<5b-n;zm2himSinw!6_KpFM&Qm1cUB<9|4lOxwY zzfdcRu$VTvJZSX9h=c~@T_?r&@jIMmmd~QBv5>k_T`Wx5D`erk+++U7(b}%cRe!r( z8~gRnA;(zL)+#m3qLjG{hJ`M9C3JZQUMjg?u#c!dSsA{%=eUvmgYlM*lhtRslD{=W z2JAmW`@Y@LX~m0eJ*%60@uGUB+zXk*_8_1XkHAv~Qp-uf6+4TKD_qrEMjR+{Nekmt~#j26OWnl$X7 zWk-yb!S@NuT++?lS}=S@AS!h_kJV~-+K212;F=rBOvg=SXHVb#iW1wQmIONDZ(qN;VpG}5 z^9>tGAw(Bjidk4GO-<}Lp*$Wrs%GTmFu$9M&9x}K53ee96swxcj;q2RI32y8)=OYG}R04lN4y=no~_&81RrNYJD aoCqV0RmYRkKj1%SLo`+O?w2WB!~Y*eWwo&Y literal 0 HcmV?d00001 diff --git a/doc/source/conf.py b/doc/source/conf.py index f7f28b23..35168207 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -163,6 +163,7 @@ nbsphinx_thumbnails = { "examples/aedt_common/api_aedt": "_static/thumbnails/coaxial.png", + "examples/properties_common/api_properties": "_static/thumbnails/book.png", } nbsphinx_custom_formats = { From c790f09eddd220085746f85a029e17fc7f2b1e35 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 15:24:14 +0100 Subject: [PATCH 11/20] Fix vale --- doc/source/index.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/source/index.rst b/doc/source/index.rst index 2b2e87b2..147d5619 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -78,15 +78,15 @@ Developers can create tools that seamlessly integrate with existing toolkits, pr and extensible ecosystem. This interoperability is crucial for users who may need functionalities from multiple toolkits in their workflows. -Code Reusability -~~~~~~~~~~~~~~~~ +Reuse code +~~~~~~~~~~ A common toolkit framework encourages the reuse of code components across different toolkits. Developers can leverage existing modules, functions, and libraries, saving time and effort in creating similar functionalities from scratch. This promotes a more efficient development process and reduces redundancy. -Maintenability -~~~~~~~~~~~~~~ +Maintain +~~~~~~~~ Standardized frameworks make it easier to maintain and update AEDT toolkits. When changes or improvements are required, developers can follow a unified set of procedures, ensuring that updates are @@ -101,14 +101,13 @@ It becomes easier for multiple developers to work on different aspects of the to as they all adhere to the same framework. This collaborative environment can lead to faster development cycles and a more robust set of tools. -Documentation and Training +Documentation and training ~~~~~~~~~~~~~~~~~~~~~~~~~~ A standardized framework comes with consistent documentation and training resources. This makes it easier for developers to understand the structure of the toolkit, its functionalities, and best practices. -It also facilitates the onboarding of new developers, as they can follow established guidelines and documentation. -Quality Assurance +Quality assurance ~~~~~~~~~~~~~~~~~ A common toolkit framework enables better quality assurance processes. From 5b6fed9cbc68a6084632630718732df701013041 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 17:13:39 +0100 Subject: [PATCH 12/20] New AEDT example --- doc/source/conf.py | 2 +- .../aedt_common/api_aedt_open_project.py | 94 +++++++++++++++++++ .../{api_aedt.py => api_aedt_simple.py} | 6 +- doc/source/examples/edb_common/api_edb.py | 2 +- doc/source/examples/index.rst | 23 ++++- 5 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 doc/source/examples/aedt_common/api_aedt_open_project.py rename doc/source/examples/aedt_common/{api_aedt.py => api_aedt_simple.py} (94%) diff --git a/doc/source/conf.py b/doc/source/conf.py index 35168207..8296dfaf 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -162,7 +162,7 @@ # Sphinx gallery customization nbsphinx_thumbnails = { - "examples/aedt_common/api_aedt": "_static/thumbnails/coaxial.png", + "examples/aedt_common/api_aedt_simple": "_static/thumbnails/coaxial.png", "examples/properties_common/api_properties": "_static/thumbnails/book.png", } diff --git a/doc/source/examples/aedt_common/api_aedt_open_project.py b/doc/source/examples/aedt_common/api_aedt_open_project.py new file mode 100644 index 00000000..f6de9c5c --- /dev/null +++ b/doc/source/examples/aedt_common/api_aedt_open_project.py @@ -0,0 +1,94 @@ +# # AEDT open project example +# +# This example shows how to use the Common AEDT API to +# launch a new AEDT session in a thread and +# open an existing AEDT project. + +# ## Perform required imports +# +# Perform required imports. + +import sys +import os +import shutil + +from pyaedt import generate_unique_folder_name + +from ansys.aedt.toolkits.common.utils import download_file +from ansys.aedt.toolkits.common.backend.api import AEDTCommon + +# ## Initialize temporary folder and project settings +# +# Initialize a temporary folder to copy the input file into +# and specify project settings. + +# + +URL_BASE = "https://raw.githubusercontent.com/ansys/example-data/master/toolkits/common/" +AEDT_PROJECT = "Test.aedt" +URL = os.path.join(URL_BASE, AEDT_PROJECT) + +temp_folder = os.path.join(generate_unique_folder_name()) + +local_project = os.path.join(temp_folder, AEDT_PROJECT) + +download_file(URL, local_project) +# - + +# ## Initialize toolkit +# +# Initialize the toolkit. + +toolkit = AEDTCommon() + +# ## Initialize AEDT +# +# Launch a new AEDT session in a thread. + +thread_msg = toolkit.launch_thread(toolkit.launch_aedt) + +# ## Wait for the toolkit thread to be idle +# +# Wait for the toolkit thread to be idle and ready to accept a new task. + +idle = toolkit.wait_to_be_idle() +if not idle: + print("AEDT not initialized.") + sys.exit() + +# ## Open project +# +# Open project. + +open_msg = toolkit.open_project(local_project) + +# ## Get toolkit properties +# +# Properties contain the project information. + +new_properties = toolkit.get_properties() + +# ## Connect design +# +# Connect or create a new design. + +toolkit.connect_design() + +# ## Create a box +# +# Create a box in the design. + +toolkit.logger.info("Create Box") +box = toolkit.aedtapp.modeler.create_box([10, 10, 10], [20, 20, 20]) +box.plot() + +# ## Save and release AEDT +# +# Save and release AEDT. + +toolkit.release_aedt(True, True) + +# ## Remove temporary folder +# +# Remove the temporary folder. + +shutil.rmtree(temp_folder, ignore_errors=True) diff --git a/doc/source/examples/aedt_common/api_aedt.py b/doc/source/examples/aedt_common/api_aedt_simple.py similarity index 94% rename from doc/source/examples/aedt_common/api_aedt.py rename to doc/source/examples/aedt_common/api_aedt_simple.py index cbc61b53..d49962fc 100644 --- a/doc/source/examples/aedt_common/api_aedt.py +++ b/doc/source/examples/aedt_common/api_aedt_simple.py @@ -1,4 +1,4 @@ -# # AEDT API example +# # AEDT simple example # # This example shows how to use the Common AEDT API to # launch a new AEDT session in a thread, @@ -57,7 +57,7 @@ new_properties = toolkit.get_properties() -# ## Create a box +# ## Create a coaxial # # Create a coaxial in the design. @@ -67,4 +67,4 @@ # # Save and release AEDT. -toolkit.release_aedt(True, True) \ No newline at end of file +toolkit.release_aedt(True, True) diff --git a/doc/source/examples/edb_common/api_edb.py b/doc/source/examples/edb_common/api_edb.py index 5efe0283..c5bd30c3 100644 --- a/doc/source/examples/edb_common/api_edb.py +++ b/doc/source/examples/edb_common/api_edb.py @@ -80,4 +80,4 @@ # # Remove the temporary folder. -shutil.rmtree(temp_folder, ignore_errors=True) \ No newline at end of file +shutil.rmtree(temp_folder, ignore_errors=True) diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index be86ee4e..900d95f1 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -2,8 +2,27 @@ Examples ======== End-to-end examples show how you can use the Common toolkit API. +Common AEDT API +~~~~~~~~~~~~~~~ +Common AEDT API examples. + +.. nbgallery:: + + aedt_common/api_aedt_simple.py + aedt_common/api_aedt_open_project.py + +Common EDB API +~~~~~~~~~~~~~~ +Common EDB API examples. + .. nbgallery:: - aedt_common/api_aedt.py edb_common/api_edb.py - properties_common/api_properties.py + +Common API +~~~~~~~~~~ +Common API examples. + +.. nbgallery:: + + properties_common/api_properties.py \ No newline at end of file From 2de69458247002754b410d093ce17120442e98cd Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 17:13:53 +0100 Subject: [PATCH 13/20] New AEDT example --- .../backend/api_aedt_open_project_example.py | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 examples/backend/api_aedt_open_project_example.py diff --git a/examples/backend/api_aedt_open_project_example.py b/examples/backend/api_aedt_open_project_example.py deleted file mode 100644 index b5f6d341..00000000 --- a/examples/backend/api_aedt_open_project_example.py +++ /dev/null @@ -1,62 +0,0 @@ -import datetime -import os -import shutil -import tempfile - -from models import properties - -from ansys.aedt.toolkits.common.backend.api import AEDTCommon - -local_path = os.path.dirname(os.path.realpath(__file__)) -test_folder = "common_toolkit_example_" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") -scratch_path = os.path.join(tempfile.gettempdir(), test_folder) -if not os.path.exists(scratch_path): - try: - os.makedirs(scratch_path) - except: - pass -example_project = shutil.copy( - os.path.join(local_path, "example_models", "Test.aedt"), os.path.join(scratch_path, "Test.aedt") -) - -# Object with generic methods to control the toolkits -toolkit = AEDTCommon(properties) - -# Get default properties -properties = toolkit.get_properties() - -# Set properties -new_properties = {"use_grpc": True} -flag1, msg1 = toolkit.set_properties(new_properties) - -# Get new properties -new_properties = toolkit.get_properties() - -# Launch AEDT. This is launched in a thread. -msg2 = toolkit.launch_thread(toolkit.launch_aedt) - -# Wait for the toolkit thread to be idle and ready to accept new task. -toolkit.wait_to_be_idle() - -# Get new properties. -new_properties = toolkit.get_properties() - -# Open project. -msg2 = toolkit.open_project(example_project) - -# Get new properties. Now the properties should contain the project information. -properties_aedt1 = toolkit.get_properties() - -# Connect to the design -flag2 = toolkit.connect_design() - -# Get new properties. Now the properties should contain the design information. -properties_aedt2 = toolkit.get_properties() - -# Create a box -toolkit.logger.info("Create Box") -box = toolkit.aedtapp.modeler.create_box([10, 10, 10], [20, 20, 20]) -box_name = box.name - -# Release aedtapp -flag3 = toolkit.release_aedt(True, True) From b428fd3bef5eb2484b42d11596cad9e70fd9e828 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Fri, 16 Feb 2024 18:35:23 +0100 Subject: [PATCH 14/20] New AEDT example --- .../aedt_common/api_aedt_connect_session.py | 94 +++++++++++++++++++ doc/source/examples/index.rst | 7 +- doc/styles/Vocab/ANSYS/accept.txt | 1 + .../api_aedt_connect_session_example.py | 54 ----------- examples/backend/backend_properties.json | 6 -- examples/backend/models.py | 40 -------- 6 files changed, 101 insertions(+), 101 deletions(-) create mode 100644 doc/source/examples/aedt_common/api_aedt_connect_session.py delete mode 100644 examples/backend/api_aedt_connect_session_example.py delete mode 100644 examples/backend/backend_properties.json delete mode 100644 examples/backend/models.py diff --git a/doc/source/examples/aedt_common/api_aedt_connect_session.py b/doc/source/examples/aedt_common/api_aedt_connect_session.py new file mode 100644 index 00000000..9e4e157c --- /dev/null +++ b/doc/source/examples/aedt_common/api_aedt_connect_session.py @@ -0,0 +1,94 @@ +# # AEDT connect session example +# +# This example shows how to use the Common AEDT API to +# connect to an existing AEDT session, +# create a HFSS design and create a waveguide. + +# ## Perform required imports +# +# Perform required imports. + +import sys +from ansys.aedt.toolkits.common.backend.api import AEDTCommon +import pyaedt + + +# ## Initialize toolkit +# +# Initialize the toolkit. + +toolkit = AEDTCommon() + +# ## Get toolkit properties +# +# Get the toolkit properties. + +properties_from_backend = toolkit.get_properties() + +# ## Initialize AEDT +# +# Initialize AEDT using PyAEDT and release it. +pyaedt.settings.enable_logger = False +app = pyaedt.Desktop(specified_version=properties_from_backend["aedt_version"], + non_graphical=properties_from_backend["non_graphical"]) +app.release_desktop(close_projects=False, close_on_exit=False) + +# ## Get AEDT sessions +# +# Get AEDT sessions and select the first one. + +sessions = toolkit.aedt_sessions() +first_key, first_value = next(iter(sessions.items())) +if first_value == -1: + use_grpc = False + selected_process = first_key +else: + use_grpc = True + selected_process = first_value + +# ## Set properties +# +# AEDT session selection. + +new_properties = {"selected_process": selected_process, "use_grpc": use_grpc} +flag, msg = toolkit.set_properties(new_properties) + +# ## Initialize AEDT +# +# Launch a new AEDT session in a thread. + +thread_msg = toolkit.launch_thread(toolkit.launch_aedt) + +# ## Wait for the toolkit thread to be idle +# +# Wait for the toolkit thread to be idle and ready to accept a new task. + +idle = toolkit.wait_to_be_idle() +if not idle: + print("AEDT not initialized.") + sys.exit() + +# ## Connect design +# +# Connect or create a new design. + +toolkit.connect_design("HFSS") + +# ## Get toolkit properties +# +# Properties contain the project information. + +new_properties = toolkit.get_properties() + +# ## Create a waveguide +# +# Create a waveguide in the design. + +wg = toolkit.aedtapp.modeler.create_waveguide([0, 0, 0], 1) +toolkit.aedtapp.plot() + +# ## Save and release AEDT +# +# Save and release AEDT. + +toolkit.release_aedt(True, True) diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index 900d95f1..a9ee6e47 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -10,6 +10,7 @@ Common AEDT API examples. aedt_common/api_aedt_simple.py aedt_common/api_aedt_open_project.py + aedt_common/api_aedt_connect_session.py Common EDB API ~~~~~~~~~~~~~~ @@ -25,4 +26,8 @@ Common API examples. .. nbgallery:: - properties_common/api_properties.py \ No newline at end of file + properties_common/api_properties.py + +REST API +~~~~~~~~ +Find here a `REST API example `_. diff --git a/doc/styles/Vocab/ANSYS/accept.txt b/doc/styles/Vocab/ANSYS/accept.txt index dc117fd9..346e0ca9 100644 --- a/doc/styles/Vocab/ANSYS/accept.txt +++ b/doc/styles/Vocab/ANSYS/accept.txt @@ -16,3 +16,4 @@ Rest_api.py Properties.json Api.py EDBCommon +EDB API diff --git a/examples/backend/api_aedt_connect_session_example.py b/examples/backend/api_aedt_connect_session_example.py deleted file mode 100644 index bc10f159..00000000 --- a/examples/backend/api_aedt_connect_session_example.py +++ /dev/null @@ -1,54 +0,0 @@ -# To run this example the user must have at least one active AEDT session. - -from models import properties - -from ansys.aedt.toolkits.common.backend.api import AEDTCommon - -# Object with generic methods to control the toolkits -toolkit = AEDTCommon(properties) - -# Get properties -new_properties = toolkit.get_properties() - -# Get AEDT sessions -sessions = toolkit.aedt_sessions() - -# Find if it is COM or GRPC -first_key, first_value = next(iter(sessions.items())) - -if first_value == -1: - use_grpc = False - selected_process = first_key -else: - use_grpc = True - selected_process = first_value - -# Set properties -new_properties = {"selected_process": selected_process, "use_grpc": use_grpc} -flag, msg = toolkit.set_properties(new_properties) - -# Get new properties -new_properties = toolkit.get_properties() - -# Launch AEDT. This is launched in a thread, so the script and the launch_aedt call run in parallel. -msg2 = toolkit.launch_thread(toolkit.launch_aedt) - -# Wait for the toolkit thread to be idle and ready to accept new task. -toolkit.wait_to_be_idle() - -# Get new properties. Now the properties should contain the project information. -new_properties = toolkit.get_properties() - -# Connect to the design -flag2 = toolkit.connect_design("HFSS") - -# Get new properties. Now the properties should contain the design information. -properties_aedt = toolkit.get_properties() - -# Create a box -toolkit.logger.info("Create Box") -box = toolkit.aedtapp.modeler.create_box([100, 10, 10], [20, 20, 20]) -box_name = box.name - -# Release aedt -flag3 = toolkit.release_aedt() diff --git a/examples/backend/backend_properties.json b/examples/backend/backend_properties.json deleted file mode 100644 index 3c3ac7e2..00000000 --- a/examples/backend/backend_properties.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "non_graphical" : false, - "log_file" : "example_backend.log", - "invented_property" : [10], - "test" : "hola" -} \ No newline at end of file diff --git a/examples/backend/models.py b/examples/backend/models.py deleted file mode 100644 index f20beff3..00000000 --- a/examples/backend/models.py +++ /dev/null @@ -1,40 +0,0 @@ -import json -import os -from typing import List - -from pydantic import BaseModel -from pydantic import Field - -from ansys.aedt.toolkits.common.backend.models import CommonProperties -from ansys.aedt.toolkits.common.backend.models import common_properties - - -class BackendProperties(BaseModel): - """Store toolkit properties.""" - - invented_property: List[int] = Field(default_factory=list) - test: str = "hola" - - -class Properties(BackendProperties, CommonProperties, validate_assignment=True): - """Store all properties.""" - - -backend_properties = {} -if os.path.expanduser(os.path.join(os.path.dirname(__file__), "backend_properties.json")): - with open(os.path.join(os.path.dirname(__file__), "backend_properties.json")) as file_handler: - backend_properties = json.load(file_handler) - -toolkit_property = {} -if backend_properties: - for backend_key in backend_properties: - if hasattr(common_properties, backend_key): - setattr(common_properties, backend_key, backend_properties[backend_key]) - else: - toolkit_property[backend_key] = backend_properties[backend_key] - -new_common_properties = {} -for common_key in common_properties: - new_common_properties[common_key[0]] = common_key[1] - -properties = Properties(**toolkit_property, **new_common_properties) From 91ed687f1a1414bfca9dfd4d301624860c769bcf Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 15:34:10 +0100 Subject: [PATCH 15/20] Update 2024R1 --- .github/workflows/ci_cd.yml | 4 ++-- examples/toolkit/pyaedt_toolkit/run_toolkit.py | 1 - src/ansys/aedt/toolkits/common/backend/api.py | 2 +- src/ansys/aedt/toolkits/common/backend/common_properties.json | 2 +- src/ansys/aedt/toolkits/common/backend/models.py | 2 +- tests/backend/conftest.py | 4 ++-- tests/backend/tests_aedt_api/conftest.py | 2 +- tests/backend/tests_common_api/conftest.py | 2 +- tests/backend/tests_edb_api/conftest.py | 2 +- tests/backend/tests_rest_api/conftest.py | 2 +- 10 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 76d6b4ed..ad1c13af 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false matrix: os: [ windows-latest, ubuntu-latest ] - python-version: [ '3.8', '3.10' ] + python-version: [ '3.10' ] runs-on: ${{ matrix.os }} needs: [ code-style ] steps: @@ -73,7 +73,7 @@ jobs: name: "Windows Tests AEDT" strategy: matrix: - python-version: [ '3.8', '3.10' ] + python-version: [ '3.10' ] fail-fast: false runs-on: [self-hosted, pyaedt, toolkits, Windows] needs: [smoke-tests] diff --git a/examples/toolkit/pyaedt_toolkit/run_toolkit.py b/examples/toolkit/pyaedt_toolkit/run_toolkit.py index ce245f80..a748133a 100644 --- a/examples/toolkit/pyaedt_toolkit/run_toolkit.py +++ b/examples/toolkit/pyaedt_toolkit/run_toolkit.py @@ -129,7 +129,6 @@ def process_desktop_properties(): "use_grpc": grpc, } requests.put(URL_CALL + "/properties", json=properties) - # requests.post(URL_CALL + "/launch_aedt") # Main Execution diff --git a/src/ansys/aedt/toolkits/common/backend/api.py b/src/ansys/aedt/toolkits/common/backend/api.py index 7e06159a..6f1b2551 100644 --- a/src/ansys/aedt/toolkits/common/backend/api.py +++ b/src/ansys/aedt/toolkits/common/backend/api.py @@ -213,7 +213,7 @@ def installed_aedt_version() -> List: >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.installed_aedt_version() - ["2022.2", "2023.2", "2024.1"] + ["2023.2", "2024.1"] """ # Detect existing AEDT installation diff --git a/src/ansys/aedt/toolkits/common/backend/common_properties.json b/src/ansys/aedt/toolkits/common/backend/common_properties.json index 1d38e17a..87365df9 100644 --- a/src/ansys/aedt/toolkits/common/backend/common_properties.json +++ b/src/ansys/aedt/toolkits/common/backend/common_properties.json @@ -1,5 +1,5 @@ { - "aedt_version" : "2023.2", + "aedt_version" : "2024.1", "non_graphical" : true, "active_project" : "", "active_design": "", diff --git a/src/ansys/aedt/toolkits/common/backend/models.py b/src/ansys/aedt/toolkits/common/backend/models.py index 288cb9b6..e703af8e 100644 --- a/src/ansys/aedt/toolkits/common/backend/models.py +++ b/src/ansys/aedt/toolkits/common/backend/models.py @@ -32,7 +32,7 @@ class CommonProperties(BaseModel): """Store common AEDT properties.""" - aedt_version: str = "2023.2" + aedt_version: str = "2024.1" non_graphical: bool = False active_project: str = "" active_design: str = "" diff --git a/tests/backend/conftest.py b/tests/backend/conftest.py index e6197e34..addf8a30 100644 --- a/tests/backend/conftest.py +++ b/tests/backend/conftest.py @@ -9,7 +9,7 @@ The default configuration can be changed by placing a file called local_config.json in the same directory as this module. An example of the contents of local_config.json { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": false, "use_grpc": true } @@ -30,7 +30,7 @@ import pytest DEFAULT_CONFIG = { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": True, "use_grpc": True, "debug": False diff --git a/tests/backend/tests_aedt_api/conftest.py b/tests/backend/tests_aedt_api/conftest.py index 93a0b578..c7e02a96 100644 --- a/tests/backend/tests_aedt_api/conftest.py +++ b/tests/backend/tests_aedt_api/conftest.py @@ -32,7 +32,7 @@ An example of the contents of local_config.json: { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": false, "use_grpc": true } diff --git a/tests/backend/tests_common_api/conftest.py b/tests/backend/tests_common_api/conftest.py index 8e337672..9ed184c2 100644 --- a/tests/backend/tests_common_api/conftest.py +++ b/tests/backend/tests_common_api/conftest.py @@ -32,7 +32,7 @@ An example of the contents of local_config.json: { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": false, "use_grpc": true } diff --git a/tests/backend/tests_edb_api/conftest.py b/tests/backend/tests_edb_api/conftest.py index d4821793..84308d5b 100644 --- a/tests/backend/tests_edb_api/conftest.py +++ b/tests/backend/tests_edb_api/conftest.py @@ -32,7 +32,7 @@ An example of the contents of local_config.json: { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": false, "use_grpc": true } diff --git a/tests/backend/tests_rest_api/conftest.py b/tests/backend/tests_rest_api/conftest.py index 7d0744af..5a487b31 100644 --- a/tests/backend/tests_rest_api/conftest.py +++ b/tests/backend/tests_rest_api/conftest.py @@ -32,7 +32,7 @@ An example of the contents of local_config.json: { - "desktop_version": "2023.2", + "desktop_version": "2024.1", "non_graphical": false, "use_grpc": true } From 314de61db75ed8c4c133b6acbbb196b22db70d47 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 16:03:33 +0100 Subject: [PATCH 16/20] Install documenation dependencies ci_cd --- .github/workflows/ci_cd.yml | 59 ++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index ad1c13af..1105d844 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -45,13 +45,64 @@ jobs: doc-build: name: "Build documentation" - runs-on: ubuntu-latest - needs: doc-style + runs-on: [self-hosted, pyaedt, toolkits, Windows] + timeout-minutes: 10 steps: - - uses: ansys/actions/doc-build@v5 + - uses: actions/checkout@v4 + + - name: "Set up Python" + uses: ansys/actions/_setup-python@main with: python-version: ${{ env.MAIN_PYTHON_VERSION }} - check-links: false + use-cache: false + + - name: 'Create virtual env' + run: | + python -m venv .venv + + - name: "Update pip" + run: | + . .venv\Scripts\Activate.ps1 + python -m pip install pip -U + python -m pip install wheel setuptools -U + python -c "import sys; print(sys.executable)" + + - name: Install common toolkit with doc dependencies + run: | + . .venv\Scripts\Activate.ps1 + pip install . + pip install .[doc] + + - name: Retrieve common toolkit version + run: | + . .venv\Scripts\Activate.ps1 + echo "Common Toolkit version is: $(python -c "from ansys.aedt.toolkits.common import __version__; print(); print(__version__)")" + + - name: Build the documentation (HTML) + run: | + .\.venv\Scripts\Activate.ps1 + cd doc + .\make.bat html + + - name: Build the documentation (PDF) + run: | + .\.venv\Scripts\Activate.ps1 + cd doc + .\make.bat pdf + + - name: Upload HTML Documentation artifact + uses: actions/upload-artifact@v4 + with: + name: documentation-html + path: doc/_build/html + retention-days: 1 + + - name: Upload PDF Documentation artifact + uses: actions/upload-artifact@v4 + with: + name: documentation-pdf + path: doc/_build/latex/*.pdf + retention-days: 7 smoke-tests: name: "Build and Smoke tests" From 0bf57321f7c5efa882f49474cd0e73ecf333fa8c Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 16:04:01 +0100 Subject: [PATCH 17/20] Install documenation dependencies ci_cd --- doc/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/index.rst b/doc/source/index.rst index 147d5619..46648ce2 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -43,7 +43,7 @@ This package provides common methods and best practices to create a new Ansys El :link: contributing :link-type: doc - Learn how to contribute to the Magnet Segmentation Toolkit codebase or documentation. + Learn how to contribute to the PyAEDT Common Toolkit codebase or documentation. .. toctree:: :hidden: From c094a5ea71f05ca2b57fb324b3d6b6a81055b487 Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 16:27:08 +0100 Subject: [PATCH 18/20] Fix vale warning --- doc/Makefile | 10 +++++++++- doc/styles/Vocab/ANSYS/accept.txt | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/Makefile b/doc/Makefile index 69e89a7e..218bea03 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -33,4 +33,12 @@ html: pdf: @$(SPHINXBUILD) -M latex "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) cd $(BUILDDIR)/latex && latexmk -r latexmkrc -pdf *.tex -interaction=nonstopmode || true - (test -f $(BUILDDIR)/latex/ansys-aedt-toolkits-common.pdf && echo pdf exists) || exit 1 + (test -f $(BUILDDIR)/latex/ansys-common-toolkit.pdf && echo pdf exists) || exit 1 + +# build docs like the CI build +cibuild: + mkdir source/examples -p + echo 'Examples' > source/examples/index.rst + echo '========' >> source/examples/index.rst + $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -j auto -w build_errors.txt -N + python print_errors.py diff --git a/doc/styles/Vocab/ANSYS/accept.txt b/doc/styles/Vocab/ANSYS/accept.txt index 346e0ca9..a1cd7f79 100644 --- a/doc/styles/Vocab/ANSYS/accept.txt +++ b/doc/styles/Vocab/ANSYS/accept.txt @@ -17,3 +17,4 @@ Properties.json Api.py EDBCommon EDB API +REST API From 75e5b1111fd06289059c399d67d390813dadffbc Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 16:48:31 +0100 Subject: [PATCH 19/20] Fix make bat --- doc/make.bat | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/make.bat b/doc/make.bat index fbf40050..5dd29eb2 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -7,11 +7,15 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) +if "%SPHINXOPTS%" == "" ( + set SPHINXOPTS = -j auto -N -q +) set SOURCEDIR=source set BUILDDIR=_build if "%1" == "" goto help if "%1" == "clean" goto clean +if "%1" == "pdf" goto pdf %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( @@ -22,7 +26,7 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://sphinx-doc.org/ exit /b 1 ) @@ -30,12 +34,23 @@ if errorlevel 9009 ( goto end :clean -rmdir /s /q %BUILDDIR% > /NUL 2>&1 +rmdir /s /q %BUILDDIR% > /NUL 2>&1 for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d" goto end +:pdf +%SPHINXBUILD% -M latex %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +cd "%BUILDDIR%\latex" +for %%f in (*.tex) do ( +xelatex "%%f" --interaction=nonstopmode) +if NOT EXIST ansys-common-toolkit.pdf ( + Echo "no pdf generated!" + exit /b 1) +Echo "pdf generated!" +goto end + :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end -popd +popd \ No newline at end of file From 5395a4b862c9fe25078499d02722754135c97e0b Mon Sep 17 00:00:00 2001 From: Samuelopez-ansys Date: Mon, 19 Feb 2024 20:43:09 +0100 Subject: [PATCH 20/20] Comment build PDF --- .github/workflows/ci_cd.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 1105d844..7abc944f 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -84,11 +84,11 @@ jobs: cd doc .\make.bat html - - name: Build the documentation (PDF) - run: | - .\.venv\Scripts\Activate.ps1 - cd doc - .\make.bat pdf +# - name: Build the documentation (PDF) +# run: | +# .\.venv\Scripts\Activate.ps1 +# cd doc +# .\make.bat pdf - name: Upload HTML Documentation artifact uses: actions/upload-artifact@v4 @@ -97,12 +97,12 @@ jobs: path: doc/_build/html retention-days: 1 - - name: Upload PDF Documentation artifact - uses: actions/upload-artifact@v4 - with: - name: documentation-pdf - path: doc/_build/latex/*.pdf - retention-days: 7 +# - name: Upload PDF Documentation artifact +# uses: actions/upload-artifact@v4 +# with: +# name: documentation-pdf +# path: doc/_build/latex/*.pdf +# retention-days: 7 smoke-tests: name: "Build and Smoke tests"