diff --git a/pyproject.toml b/pyproject.toml index c3dcede0d..5a331f322 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dependencies = [ "requests >= 2.27.1", "packaging >= 21.3", "importlib-metadata>=4.0", + "openpyxl >=3.1.5", ] [project.optional-dependencies] @@ -38,7 +39,10 @@ tests = [ "pytest==8.3.5", "pytest-cov==6.0.0", "ansys.platform.instancemanagement>=1.0.2", - "matplotlib>=3.6.3" + "matplotlib>=3.6.3", + "scipy", + "openpyxl >=3.1.5", + ] doc = [ "Sphinx==8.1.3", diff --git a/src/ansys/motorcad/core/methods/rpc_methods_lab.py b/src/ansys/motorcad/core/methods/rpc_methods_lab.py index 76a3918d4..7df1c4a8a 100644 --- a/src/ansys/motorcad/core/methods/rpc_methods_lab.py +++ b/src/ansys/motorcad/core/methods/rpc_methods_lab.py @@ -33,6 +33,17 @@ k_custom_loss_voltage_function_external_lab = "CustomLoss_VoltageFunction_External_Lab" +from openpyxl import Workbook + +try: + import numpy as np + from scipy.io import loadmat + + Num_Sci_py_AVAILABLE = True +except ImportError: + Num_Sci_py_AVAILABLE = False + + class _RpcMethodsLab: def __init__(self, mc_connection): self.connection = mc_connection @@ -304,3 +315,161 @@ def export_lab_model(self, file_path): method = "ExportLabModel" params = [file_path] return self.connection.send_and_receive(method, params) + + def _write_excel_IM_speed(data, sheets, DC_voltage_list, i, wb): + i_len, j_len = data["Speed"].shape + for sheet in sheets: + ws = wb.create_sheet("Newsheet") + ws.title = "Voltages" + if len(DC_voltage_list) > 1: + ws.title = sheet + str(i + 1) + else: + ws.title = sheet + if sheet == "Speed": + for jj, col in enumerate(ws.iter_cols(min_col=0, max_col=j_len, max_row=i_len)): + for ii, cell in enumerate(col): + if data[sheet][ii][jj] == 1: + ws[cell.coordinate] = 0 + else: + ws[cell.coordinate] = data[sheet][ii][jj] + else: + for jj, col in enumerate(ws.iter_cols(min_col=0, max_col=j_len, max_row=i_len)): + for ii, cell in enumerate(col): + ws[cell.coordinate] = data[sheet][ii][jj] + return wb + + def _write_excel_BPM(data, sheets, DC_voltage_list, i, wb): + i_len, j_len = data["Speed"].shape + for sheet in sheets: + ws = wb.create_sheet("Newsheet") + ws.title = "Voltages" + if len(DC_voltage_list) > 1: + ws.title = sheet + str(i + 1) + else: + ws.title = sheet + for jj, col in enumerate(ws.iter_cols(min_col=0, max_col=j_len, max_row=i_len)): + for ii, cell in enumerate(col): + ws[cell.coordinate] = data[sheet][ii][jj] + return wb + + def _set_model_parameters(self, **kwargs): + if "Max_speed" in kwargs: + self.set_variable("SpeedMax_MotorLAB", kwargs["Max_speed"]) + if "Min_speed" in kwargs: + if self.get_variable("Motor_Type") == 1 and kwargs["Min_speed"] == 0: + self.set_variable("SpeedMin_MotorLAB", 1) + else: + self.set_variable("SpeedMin_MotorLAB", kwargs["Min_speed"]) + if "Speed_Step" in kwargs: + self.set_variable("Speedinc_MotorLAB", kwargs["Speed_Step"]) + + Current_def = self.get_variable("CurrentSpec_MotorLAB") + if Current_def == 0: # peak + if "I_max" in kwargs: + if self.get_variable("Motor_Type") == 6: # Sync + self.set_variable("Sync_StatorCurrentMax_Lab", kwargs["I_max"]) + else: + self.set_variable("Imax_MotorLAB", kwargs["I_max"]) + if "I_min" in kwargs: + self.set_variable("Imin_MotorLAB", kwargs["I_min"]) + # if "I_inc" in kwargs: + # self.set_variable("Iinc_MotorLAB",kwargs["I_inc"]) + else: # RMS + if "I_max" in kwargs: + if self.get_variable("Motor_Type") == 6: # Sync + self.set_variable("Sync_StatorCurrentMax_RMS_Lab", kwargs["I_max"]) + else: + self.set_variable("Imax_RMS_MotorLAB", kwargs["I_max"]) + if "I_min" in kwargs: + self.set_variable("Imin_RMS_MotorLAB", kwargs["I_min"]) + if "I_inc" in kwargs: + if self.get_variable("Motor_Type") == 6: # Sync machine + print("sync executed") + self.set_variable("Sync_CurrentIncs_Lab", kwargs["I_inc"]) + else: + self.set_variable("Iinc_MotorLAB", kwargs["I_inc"]) + + # choose motoring, generating or both modes + if "Rotor_current_max" in kwargs: + self.set_variable("Sync_RotorCurrentMax_Lab", kwargs["Rotor_current_max"]) + + if "Op_mode" in kwargs: + self.set_variable("OperatingMode_Lab", kwargs["Op_mode"]) + + def export_concept_ev_model(self, **kwargs): + """Export efficiency map in concept ev excel format. + + Parameters + ---------- + Max_speed : int + Maximum speed in electromagnetic calculation + Min_speed : int + Minimum speed in electromagnetic calculation + Speed_step : int + Speed increment in electromagnetic calculation + I_max : float + Maximum current (peak or rms based on settings) + I_min : float + Minimum current (peak or rms based on settings) + I_inc : float + Current increment in electromagnetic calculation + Rotor_current_max: float + Maximum rotor current in electromagnetic calculation (only in Sync machines) + Op_mode: int + 0 Motor, 1 Generator, 2 Motor / Generator mode + DC_voltage_list: list + List of DC bus voltages + """ + if not Num_Sci_py_AVAILABLE: + raise ImportError( + "Failed to export concept_ev_model. Please ensure Numpy and Scipy are installed" + ) + + self.set_variable("MessageDisplayState", 2) + self.set_motorlab_context() + file_path = self.get_variable("ResultsPath_MotorLAB") + "ConceptEV_elecdata.xlsx" + # set model parameters + _RpcMethodsLab._set_model_parameters(self, **kwargs) + wb = Workbook() + # choose number of DC bus voltages (list as user input) + if "DC_voltage_list" in kwargs: + DC_voltage_list = kwargs["DC_voltage_list"] + else: + DC_voltage_list = [self.get_variable("DCBusVoltage")] + + ws = wb.active + ws.title = "Voltages" + ws["A1"] = "Index" + ws["B1"] = "Voltages" + for i, DC_voltage in enumerate(DC_voltage_list): + ws["A" + str(i + 2)] = i + 1 + ws["B" + str(i + 2)] = DC_voltage_list[i] + + # Units sheet + + # set _calcualtion type Efficiency Map + self.set_variable("EmagneticCalcType_Lab", 1) + sheets = ["Speed", "Shaft_Torque", "Stator_Current_Line_RMS", "Total_Loss", "Power_Factor"] + for i, DC_voltage in enumerate(DC_voltage_list): + self.set_variable("DCBusVoltage", DC_voltage) + # run Efficiency Map calculation + self.calculate_magnetic_lab() + # read the lab data .mat file + data_file_path = self.get_variable("ResultsPath_MotorLAB") + "MotorLAB_elecdata.mat" + data = loadmat(data_file_path) + if "Min_speed" in kwargs: + if self.get_variable("Motor_Type") == 1 and kwargs["Min_speed"] == 0: + wb = _RpcMethodsLab._write_excel_IM_speed(data, sheets, DC_voltage_list, i, wb) + else: + wb = _RpcMethodsLab._write_excel_BPM(data, sheets, DC_voltage_list, i, wb) + else: + wb = _RpcMethodsLab._write_excel_BPM(data, sheets, DC_voltage_list, i, wb) + + units = ["Power_Factor", "Total_Loss", "Stator_Current_Line_RMS", "Shaft_Torque", "Speed"] + ws = wb.create_sheet("Newsheet") + ws.title = "Units" + for i, unit in enumerate(units): + ws["A" + str(i + 1)] = unit + index = np.where(np.strings.find(data["varStr"], unit) == 0)[0] + ws["B" + str(i + 1)] = data["varUnits"][index][0] + wb.save(file_path) diff --git a/tests/test_lab.py b/tests/test_lab.py index 8d3d4a2e6..e54626a59 100644 --- a/tests/test_lab.py +++ b/tests/test_lab.py @@ -23,9 +23,12 @@ # from os import path, remove # import time +import os + +from openpyxl import load_workbook import pytest -from RPC_Test_Common import reset_to_default_file # (get_dir_path, +from RPC_Test_Common import almost_equal_percentage, reset_to_default_file # (get_dir_path, def test_model_build_lab(mc): @@ -172,6 +175,29 @@ def test_external_custom_loss_functions(mc): assert mc.get_variable("NumCustomLossesExternal_Lab") == no_external_losses + 1 +def test_export_concept_ev_model(mc): + mc.set_variable("MessageDisplayState", 2) + mc.load_template("e9") + # run Efficiency Map calculation + mc.calculate_magnetic_lab() + file_path = mc.get_variable("ResultsPath_MotorLAB") + "ConceptEV_elecdata.xlsx" + mc.export_concept_ev_model() + assert os.path.exists(file_path) is True + wb = load_workbook(file_path) + assert "Shaft_Torque" in wb.sheetnames + assert "Voltages" in wb.sheetnames + assert "Speed" in wb.sheetnames + assert "Stator_Current_Line_RMS" in wb.sheetnames + assert "Total_Loss" in wb.sheetnames + assert "Power_Factor" in wb.sheetnames + assert "Units" in wb.sheetnames + speed_sheet = wb["Speed"] + assert speed_sheet.max_row == 21 + assert speed_sheet.max_column == 60 + torque_sheet = wb["Shaft_Torque"] + assert almost_equal_percentage(torque_sheet.cell(row=1, column=1).value, 274.16, 0.1) + + # def test_lab_model_export(mc): # mc.set_variable("MessageDisplayState", 2) # file_path = get_dir_path() + r"\test_files\temp_files\lab_model_export.lab"