Source code for ansys.aedt.toolkits.electronic_transformer.backend.api

# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from pathlib import Path
import sys

from ansys.aedt.core import Maxwell3d

sys.path.append(str(Path(__file__).parent))

from datetime import datetime
import json

from ansys.aedt.toolkits.common.backend.api import AEDTCommon
from ansys.aedt.toolkits.common.backend.logger_handler import logger
from ansys.aedt.toolkits.electronic_transformer.backend.models import BackendResetProperties
from ansys.aedt.toolkits.electronic_transformer.backend.models import ExcitationType
from ansys.aedt.toolkits.electronic_transformer.backend.models import Layer
from ansys.aedt.toolkits.electronic_transformer.backend.models import Material
from ansys.aedt.toolkits.electronic_transformer.backend.models import properties
from ansys.aedt.toolkits.electronic_transformer.backend.workflows.bobbin import Bobbin
from ansys.aedt.toolkits.electronic_transformer.backend.workflows.core import Core
from ansys.aedt.toolkits.electronic_transformer.backend.workflows.etk import ETK
from ansys.aedt.toolkits.electronic_transformer.backend.workflows.winding import Winding
from ansys.aedt.toolkits.electronic_transformer.common.validation import Validation


[docs] class ToolkitBackend(AEDTCommon): """Control the toolkit workflow. This class provides methods to connect to a selected design and create the model or the parts of the transformer. Examples -------- >>> from ansys.aedt.toolkits.electronic_transformer.backend.api import ToolkitBackend >>> toolkit_api = ToolkitBackend() >>> toolkit_api.launch_aedt() >>> toolkit_api.wait_to_be_idle() >>> toolkit_api.create_core_geometry() """ def __init__(self): """Initialize the ``ToolkitBackend`` class.""" self.__reset_transformer_properties() AEDTCommon.__init__(self, properties) self.__input_props = None self.__validator = Validation() def __reset_transformer_properties(self): be_properties = BackendResetProperties() properties.core = be_properties.core properties.winding = be_properties.winding properties.bobbin = be_properties.bobbin properties.settings = be_properties.settings properties.materials = be_properties.materials properties.circuit = be_properties.circuit # properties.aedt_version: str = DEFAULT_AEDT_VERSION properties.non_graphical = False properties.active_project = "" properties.active_design = "" properties.project_list = [] properties.design_list = {} properties.selected_process = 0 properties.use_grpc = True properties.is_toolkit_busy = False properties.url = "127.0.0.1" properties.port = 5001 properties.debug = True properties.toolkit_name = "common" properties.log_file = "common_backend.log" properties.state = "" properties.progress = 0 def __update_core_properties(self): """Update core properties from the input data.""" core_props = self.__input_props["core"] self.properties.core.supplier = core_props["supplier"] self.properties.core.type = core_props["type"] self.properties.core.model = core_props["model"] self.properties.core.material = core_props["material"] self.properties.core.dimensions = core_props["dimensions"] self.properties.core.airgap.define_airgap = core_props["airgap"]["define_airgap"] if self.properties.core.airgap.define_airgap: self.properties.core.airgap.airgap_on_leg = core_props["airgap"]["airgap_on_leg"] self.properties.core.airgap.airgap_value = core_props["airgap"]["airgap_value"] def __update_material_properties(self): """Update material properties from the input data.""" materials = self.__input_props["materials"] for key_material, value_material in materials.items(): # Creates a default material this_material = Material() this_material.name = key_material # Updates the default material only if there is data in the json file if "conductivity" in value_material: this_material.conductivity = float(value_material["conductivity"]) if "density" in value_material: this_material.density = float(value_material["density"]) if "mur" in value_material: if float(value_material["mur"]) == 0: this_material.mur = 1.0 else: this_material.mur = float(value_material["mur"]) if "epsr" in value_material: this_material.epsr = float(value_material["epsr"]) if "power_ferrite_loss_params" in value_material: this_material.power_ferrite_loss_params.cm = float(value_material["power_ferrite_loss_params"]["cm"]) this_material.power_ferrite_loss_params.x = float(value_material["power_ferrite_loss_params"]["x"]) this_material.power_ferrite_loss_params.y = float(value_material["power_ferrite_loss_params"]["y"]) if "mu_vs_freq" in value_material: this_material.mu_vs_freq = value_material["mu_vs_freq"] self.properties.materials[key_material] = this_material def __update_winding_properties(self): """Update winding properties from the input data.""" winding_props = self.__input_props["winding"] self.properties.winding.layer_type = winding_props["layer_type"] self.properties.winding.layer_spacing = winding_props["layer_spacing"] self.properties.winding.top_margin = winding_props["top_margin"] self.properties.winding.side_margin = winding_props["side_margin"] # Layers for key_layer, value_layer in winding_props["layers"].items(): this_layer = Layer() if "insulation" in value_layer: this_layer.insulation.material = value_layer["insulation"]["material"] this_layer.insulation.thickness = value_layer["insulation"]["thickness"] this_layer.conductor.draw_skin_layers = value_layer["conductor"]["draw_skin_layers"] this_layer.conductor.material = value_layer["conductor"]["material"] this_layer.conductor.type = value_layer["conductor"]["type"] if this_layer.conductor.type == "Rectangular": this_layer.conductor.height = value_layer["conductor"]["height"] this_layer.conductor.width = value_layer["conductor"]["width"] elif this_layer.conductor.type == "Circular": this_layer.conductor.diameter = value_layer["conductor"]["diameter"] this_layer.turns.quantity = value_layer["turns"]["quantity"] this_layer.turns.spacing = value_layer["turns"]["spacing"] this_layer.turns.distance = value_layer["turns"]["distance"] self.properties.winding.layers[key_layer] = this_layer def __update_bobbin_properties(self): """Update bobbin properties from the input data.""" bobbin_props = self.__input_props["bobbin"] self.properties.bobbin.draw_bobbin = bobbin_props["draw_bobbin"] self.properties.bobbin.material = bobbin_props["material"] self.properties.bobbin.board_thickness = bobbin_props["board_thickness"] def __update_settings_properties(self): """Update settings properties from the input data.""" setup_props = self.__input_props["settings"] self.properties.settings.full_model = setup_props["full_model"] self.properties.settings.region_offset = setup_props["region_offset"] self.properties.settings.segmentation_angle = setup_props["segmentation_angle"] self.properties.settings.analysis_setup.adaptive_frequency = setup_props["analysis_setup"]["adaptive_frequency"] self.properties.settings.analysis_setup.percentage_error = setup_props["analysis_setup"]["percentage_error"] self.properties.settings.analysis_setup.number_passes = setup_props["analysis_setup"]["number_passes"] self.properties.settings.analysis_setup.frequency_sweep.frequency_sweep = setup_props["analysis_setup"][ "frequency_sweep" ]["frequency_sweep"] self.properties.settings.analysis_setup.frequency_sweep.start_frequency = setup_props["analysis_setup"][ "frequency_sweep" ]["start_frequency"] self.properties.settings.analysis_setup.frequency_sweep.stop_frequency = setup_props["analysis_setup"][ "frequency_sweep" ]["stop_frequency"] self.properties.settings.analysis_setup.frequency_sweep.samples = setup_props["analysis_setup"][ "frequency_sweep" ]["samples"] self.properties.settings.analysis_setup.frequency_sweep.scale = setup_props["analysis_setup"][ "frequency_sweep" ]["scale"] def __update_circuit_properties(self): """Update circuit properties from the input data.""" circuit_props = self.__input_props["circuit"] self.properties.circuit.connections = circuit_props["connections"] self.properties.circuit.side_loads = circuit_props["side_loads"] self.properties.circuit.excitation.value = circuit_props["excitation"]["value"] if circuit_props["excitation"]["type"].lower() == "voltage": self.properties.circuit.excitation.type = ExcitationType.voltage elif circuit_props["excitation"]["type"].lower() == "current": self.properties.circuit.excitation.type = ExcitationType.current def __update_json_version(self): """Update circuit properties from the input data.""" self.properties.json_version = self.__input_props["json_version"]
[docs] def load_properties_from_json(self, file_path): """Load properties from a JSON file. Parameters ---------- file_path : str Path to the JSON file. """ with Path.open(file_path) as f: json_read = json.load(f) self.__input_props = json_read self.__validator.validate_json(self.__input_props) self.__update_core_properties() self.__update_winding_properties() self.__update_bobbin_properties() self.__update_settings_properties() self.__update_material_properties() self.__update_circuit_properties() self.__update_json_version()
def __update_props_from_frontend(self, frontend_properties): """Update properties from a the frontend. Parameters ---------- frontend_properties : dict Dictionary with the FE data. """ self.__input_props = { "json_version": frontend_properties["json_version"], "winding": frontend_properties["winding"], "core": frontend_properties["core"], "bobbin": frontend_properties["bobbin"], "settings": frontend_properties["settings"], "materials": frontend_properties["materials"], "circuit": frontend_properties["circuit"], } self.__validator.validate_json(self.__input_props) self.__update_core_properties() self.__update_winding_properties() self.__update_bobbin_properties() self.__update_settings_properties() self.__update_material_properties() self.__update_circuit_properties() self.__update_json_version()
[docs] def create_core_geometry(self, frontend_properties=None): """Create the core geometry. Parameters ---------- frontend_properties : dict, default: None Frontend properties. Returns ------- :class:`ansys.aedt.toolkits.electronic_transformer.backend.workflows.core.Core` Core object. """ if frontend_properties: self.__update_props_from_frontend(frontend_properties) self.connect_design("Maxwell3D") if self.aedtapp is None: logger.error("Not connected.") return False ocore = Core("Core", self.aedtapp, properties.core, properties.settings) ocore.create_geometry() self.release_aedt() if not ocore: logger.error("Core not created.") return False return ocore
[docs] def create_winding_geometry(self, frontend_properties=None): """Create the winding geometry. Parameters ---------- frontend_properties : dict, default: None Frontend properties. Returns ------- :class:`ansys.aedt.toolkits.electronic_transformer.backend.workflows.winding.Winding` Winding object. """ # Updates the backend data structure if frontend_properties: self.__update_props_from_frontend(frontend_properties) self.connect_design("Maxwell3D") if self.aedtapp is None: logger.error("Not connected.") return False owinding = Winding( "Windings", self.aedtapp, properties.winding, properties.core, properties.settings, properties.bobbin, ) owinding.create_geometry() self.release_aedt() if not owinding: logger.error("Windings not created.") return False return owinding
[docs] def create_bobbin_geometry(self, frontend_properties=None): """Create the bobbin geometry. Parameters ---------- frontend_properties : dict, default: None Frontend properties. Returns ------- :class:`ansys.aedt.toolkits.electronic_transformer.backend.workflows.bobbin.Bobbin` Bobbin object. """ # Updates the backend data structure if frontend_properties: self.__update_props_from_frontend(frontend_properties) self.connect_design("Maxwell3D") if self.aedtapp is None: logger.error("Not connected.") return False obobbin = Bobbin( "Bobbin", self.aedtapp, properties.bobbin, properties.core, properties.winding, properties.settings ) obobbin.create_geometry() self.release_aedt() if not obobbin: logger.error("Bobbin not created.") return False return obobbin
def __validate_model(self): """Validate the model configuration. Considers that the properties are already read. Returns ------- list List of validation error messages. Empty list if validation passes. """ validator = Validation() error_messages = validator.validate_model( properties.core, properties.winding, properties.bobbin, ) if len(error_messages) > 0: for msg in error_messages: logger.error(msg) return False, error_messages return True, error_messages
[docs] def validate_model(self, frontend_properties=None): """Validate the transformer model configuration. Parameters ---------- frontend_properties : dict, optional Frontend properties. The default is ``None``. Returns ------- tuple Tuple containing validation status (bool) and list of error messages. """ if frontend_properties: self.__update_props_from_frontend(frontend_properties) return self.__validate_model()
[docs] def create_model(self, frontend_properties=None): """Create the complete model. Parameters ---------- frontend_properties : dict, default: None Frontend properties. Returns ------- :class:`ansys.aedt.toolkits.electronic_transformer.backend.workflows.etk.ETK` ETK object. """ # Updates the backend data structure if frontend_properties: self.__update_props_from_frontend(frontend_properties) # Validates the model validated, error_messages = self.__validate_model() if not validated: return False self.__create_maxwell_design() self.connect_design("Maxwell3D") if self.aedtapp is None: logger.error("Not connected.") return False oetk = ETK( self.aedtapp, properties.core, properties.settings, properties.winding, properties.materials, properties.bobbin, properties.circuit, ) oetk.create_model() self.release_aedt() if not oetk: logger.error("Model not created.") return False return True
[docs] def validate_json(self, data): """Validate input data from a JSON file. Parameters ---------- data : :class:`pathlib.Path` Path to the input data in JSON format. """ validator = Validation() validator.validate_json(data)
def __create_maxwell_design(self): """Create a Maxwell design. Creates a Maxwell design in running AEDT instance. """ core_type = self.__input_props["core"]["type"] build_type = self.__input_props["winding"]["layer_type"] self.properties.active_project = "PyETK Project" self.properties.project_list = [self.properties.active_project] # update active design and add it to the list self.properties.active_design = f"{core_type}_{build_type}_{datetime.now().strftime('%Y%m%d%H%M%S')}" self.properties.design_list.setdefault(self.properties.active_project, []).append(self.properties.active_design) Maxwell3d(project=self.properties.active_project, design=self.properties.active_design)