From 2a62fa5101a3df3ff13000f332917667d48017a8 Mon Sep 17 00:00:00 2001 From: gmalinve <103059376+gmalinve@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:26:53 +0100 Subject: [PATCH 1/2] Core loss electrical steel (#3829) * enhance core loss electrical steel * implement core losses * fix * set_electrical_steel core losses with coefficients * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove imports * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add get coreloss coefficients test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * set core loss test implement * Update pyaedt/modules/Material.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update pyaedt/modules/Material.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update pyaedt/modules/Material.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update pyaedt/modules/Material.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyaedt/modules/Material.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * fix comments review * Apply suggestions from code review Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com> * fix comments review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix comments review * fix docstring * Fix return list * fix regression error --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com> Co-authored-by: Samuelopez-ansys --- _unittest/test_03_Materials.py | 109 ++++++++++++- pyaedt/modules/Material.py | 285 +++++++++++++++++++++++++++++++-- 2 files changed, 381 insertions(+), 13 deletions(-) diff --git a/_unittest/test_03_Materials.py b/_unittest/test_03_Materials.py index 560ee092fd9..16e15f4df48 100644 --- a/_unittest/test_03_Materials.py +++ b/_unittest/test_03_Materials.py @@ -82,7 +82,7 @@ def test_02_create_material(self): assert mat1.set_electrical_steel_coreloss(1, 2, 3, 4, 0.002) assert mat1.get_curve_coreloss_type() == "Electrical Steel" - assert mat1.get_curve_coreloss_values()["core_loss_equiv_cut_depth"] == "0.002meter" + assert mat1.get_curve_coreloss_values()["core_loss_equiv_cut_depth"] == 0.002 assert mat1.set_hysteresis_coreloss(1, 2, 3, 4, 0.002) assert mat1.get_curve_coreloss_type() == "Hysteresis Model" assert mat1.set_bp_curve_coreloss([[0, 0], [10, 10], [20, 20]]) @@ -210,7 +210,7 @@ def test_08B_import_materials_from_excel(self): assert len(mats) == 2 def test_09_non_linear_materials(self, add_app): - app = add_app(application=Maxwell3d) + app = add_app(application=Maxwell3d, solution_type="Transient") mat1 = app.materials.add_material("myMat") mat1.permeability = [[0, 0], [1, 12], [10, 30]] mat1.permittivity = [[0, 0], [2, 12], [10, 30]] @@ -274,3 +274,108 @@ def test_13_get_materials_in_project(self): assert isinstance(used_materials, list) for m in [mat for mat in self.aedtapp.materials if mat.is_used]: assert m.name in used_materials + + def test_14_get_coreloss_coefficients(self): + mat = self.aedtapp.materials.add_material("mat_test") + # Test points_list_at_freq + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]} + ) + assert isinstance(coeff, list) + assert len(coeff) == 3 + assert all(isinstance(c, float) for c in coeff) + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={"60Hz": [[0, 0], [1, 3.5], [2, 7.4]]} + ) + assert isinstance(coeff, list) + assert len(coeff) == 3 + assert all(isinstance(c, float) for c in coeff) + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={"0.06kHz": [[0, 0], [1, 3.5], [2, 7.4]]} + ) + assert isinstance(coeff, list) + assert len(coeff) == 3 + assert all(isinstance(c, float) for c in coeff) + try: + self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq=[[0, 0], [1, 3.5], [2, 7.4]] + ) + assert False + except TypeError: + assert True + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={ + 60: [[0, 0], [1, 3.5], [2, 7.4]], + 100: [[0, 0], [1, 8], [2, 9]], + 150: [[0, 0], [1, 10], [2, 19]], + } + ) + assert isinstance(coeff, list) + assert len(coeff) == 3 + assert all(isinstance(c, float) for c in coeff) + # Test thickness + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness="0.6mm" + ) + assert isinstance(coeff, list) + assert len(coeff) == 3 + assert all(isinstance(c, float) for c in coeff) + try: + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness="invalid" + ) + assert False + except TypeError: + assert True + try: + coeff = self.aedtapp.materials["mat_test"].get_core_loss_coefficients( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness=50 + ) + assert False + except TypeError: + assert True + + def test_14_set_core_loss(self): + mat = self.aedtapp.materials["mat_test"] + # Test points_list_at_freq + assert self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]} + ) + assert self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={"60Hz": [[0, 0], [1, 3.5], [2, 7.4]]} + ) + assert self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={"0.06kHz": [[0, 0], [1, 3.5], [2, 7.4]]} + ) + try: + self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq=[[0, 0], [1, 3.5], [2, 7.4]] + ) + assert False + except TypeError: + assert True + assert self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={ + 60: [[0, 0], [1, 3.5], [2, 7.4]], + 100: [[0, 0], [1, 8], [2, 9]], + 150: [[0, 0], [1, 10], [2, 19]], + } + ) + # Test thickness + assert self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness="0.6mm" + ) + try: + coeff = self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness="invalid" + ) + assert False + except TypeError: + assert True + try: + coeff = self.aedtapp.materials["mat_test"].set_coreloss_at_frequency( + points_list_at_freq={60: [[0, 0], [1, 3.5], [2, 7.4]]}, thickness=50 + ) + assert False + except TypeError: + assert True diff --git a/pyaedt/modules/Material.py b/pyaedt/modules/Material.py index a768baacb9a..14940bb3e18 100644 --- a/pyaedt/modules/Material.py +++ b/pyaedt/modules/Material.py @@ -16,8 +16,11 @@ import copy import warnings +from pyaedt.application.Variables import decompose_variable_value from pyaedt.generic.DataHandlers import _dict2arg from pyaedt.generic.constants import CSS4_COLORS +from pyaedt.generic.constants import unit_converter +from pyaedt.generic.general_methods import is_number from pyaedt.generic.general_methods import pyaedt_function_handler @@ -1157,7 +1160,7 @@ def _get_args(self, props=None): Parameters ---------- - prop : str, optional + props : str, optional Name of the property. The default is ``None``. """ if not props: @@ -1992,16 +1995,277 @@ def set_magnetic_coercivity(self, value=0, x=1, y=0, z=0): return self.update() @pyaedt_function_handler() - def set_electrical_steel_coreloss(self, kh=0, kc=0, ke=0, kdc=0, cut_depth=0.0001): - """Set Electrical Steel Type Core Loss. + def get_core_loss_coefficients( + self, + points_list_at_freq, + core_loss_model_type="Electrical Steel", + thickness="0.5mm", + conductivity=0, + coefficient_setup="w_per_cubic_meter", + ): + """Get electrical steel or power ferrite core loss coefficients at a given frequency. + + Parameters + ---------- + points_list_at_freq : dict + Dictionary where keys are the frequencies (in Hz) and values are lists of points (BP curve). + If the core loss model is calculated at one frequency, this parameter must be provided as a + dictionary with one key (single frequency in Hz) and values are lists of points at + that specific frequency (BP curve). + core_loss_model_type : str, optional + Core loss model type. The default value is ``"Electrical Steel"``. + Options are ``"Electrical Steel"`` and ``"Power Ferrite"``. + thickness : str, optional + Thickness provided as the value plus the unit. + The default is ``0.5mm``. + conductivity : float, optional + Material conductivity. + The default is ``0``. + coefficient_setup : str, optional + Core loss unit. The default is ``"w_per_cubic_meter"``. + Options are ``"kw_per_cubic_meter"``, ``"w_per_cubic_meter"``, ``"w_per_kg"``, + and ``"w_per_lb"``. + + + Returns + ------- + list + List of core loss coefficients. + Returns Kh, Kc, and Ke coefficients if the core loss model is ``"Electrical Steel"``. + Returns Cm, X, and Y if the core loss model is ``"Power Ferrite"``. + + Examples + -------- + This example shows how to get core loss coefficients for Electrical Steel core loss model. + + >>> from pyaedt import Maxwell3d + >>> m3d = Maxwell3d() + >>> box = m3d.modeler.create_box([-10, -10, 0], [20, 20, 20], "box_to_split") + >>> box.material = "magnesium" + >>> coefficients = m3d.materials["magnesium"].get_core_loss_coefficients( + ... points_list_at_freq={60 : [[0, 0], [1, 3], [2, 7]]}, + ... thickness="0.5mm", + ... conductivity=0) + >>> print(coefficients) + >>> m3d.release_desktop(True, True) + """ + if not isinstance(points_list_at_freq, dict): + raise TypeError("Points list at frequency must be provided as a dictionary.") + if not isinstance(thickness, str): + raise TypeError("Thickness must be provided as a string with value and unit.") + else: + value, unit = decompose_variable_value(thickness) + if not is_number(value) and not unit: + raise TypeError("Thickness must be provided as a string with value and unit.") + props = OrderedDict({}) + freq_keys = list(points_list_at_freq.keys()) + for i in range(0, len(freq_keys)): + if isinstance(freq_keys[i], str): + value, unit = decompose_variable_value(freq_keys[i]) + if unit != "Hz": + value = unit_converter(values=value, unit_system="Freq", input_units=unit, output_units="Hz") + points_list_at_freq[value] = points_list_at_freq[freq_keys[i]] + del points_list_at_freq[freq_keys[i]] + + if len(points_list_at_freq) == 1: + props["CoefficientSetupData"] = OrderedDict({}) + props["CoefficientSetupData"]["property_data"] = "coreloss_data" + props["CoefficientSetupData"]["coefficient_setup"] = coefficient_setup + frequency = list(points_list_at_freq.keys())[0] + props["CoefficientSetupData"]["Frequency"] = "{}Hz".format(frequency) + props["CoefficientSetupData"]["Thickness"] = thickness + props["CoefficientSetupData"]["Conductivity"] = str(conductivity) + points = [i for p in points_list_at_freq[frequency] for i in p] + props["CoefficientSetupData"]["Coordinates"] = OrderedDict({"DimUnits": ["", ""], "Points": points}) + elif len(points_list_at_freq) > 1: + props["CoreLossMultiCurveData"] = OrderedDict({}) + props["CoreLossMultiCurveData"]["property_data"] = "coreloss_multi_curve_data" + props["CoreLossMultiCurveData"]["coreloss_unit"] = "w_per_cubic_meter" + + props["CoreLossMultiCurveData"]["AllCurves"] = OrderedDict({}) + props["CoreLossMultiCurveData"]["AllCurves"]["OneCurve"] = [] + for freq in points_list_at_freq.keys(): + points = [i for p in points_list_at_freq[freq] for i in p] + one_curve = OrderedDict( + { + "Frequency": "{}Hz".format(freq), + "Coordinates": OrderedDict({"DimUnits": ["", ""], "Points": points}), + } + ) + props["CoreLossMultiCurveData"]["AllCurves"]["OneCurve"].append(one_curve) + + props = self._get_args(props) + props.pop(0) + props[0][-1][2] = "NAME:Points" + points = props[0][-1].pop(2) + props[0][-1][2].insert(0, points) + coefficients = self.odefinition_manager.ComputeCoreLossCoefficients( + core_loss_model_type, self.mass_density.evaluated_value, props[0] + ) + return list(coefficients) + + @pyaedt_function_handler() + def set_coreloss_at_frequency( + self, + points_list_at_freq, + kdc=0, + cut_depth="1mm", + thickness="0.5mm", + conductivity=0, + coefficient_setup="w_per_cubic_meter", + core_loss_model_type="Electrical Steel", + ): + """Set electrical steel or power ferrite core loss model at one single frequency or at multiple frequencies. Parameters ---------- - kh : float - kc : float - ke : float + points_list_at_freq : dict + Dictionary where keys are the frequencies (in Hz) and values are lists of points (BP curve). + If the core loss model is calculated at one frequency, this parameter must be provided as a + dictionary with one key (single frequency in Hz) and values are lists of points at + that specific frequency (BP curve). kdc : float - cut_depth : float + Coefficient considering the DC flux bias effects + cut_depth : str, optional + Equivalent cut depth. + You use this parameter to consider the manufacturing effects on core loss computation. + The default value is ``"1mm"``. + thickness : str, optional + Thickness specified in terms of the value plus the unit. + The default is ``"0.5mm"``. + conductivity : float, optional + Conductivity. The unit is S/m. + The default is ``"0 S/m"``. + coefficient_setup : str, optional + Core loss unit. The default is ``"w_per_cubic_meter"``. + Options are ``"kw_per_cubic_meter"``, ``"w_per_cubic_meter"``, ``"w_per_kg"``, + and ``"w_per_lb"``. + core_loss_model_type : str, optional + Core loss model type. The default value is ``"Electrical Steel"``. + Options are ``"Electrical Steel"`` and ``"Power Ferrite"``. + + Returns + ------- + bool + ``True`` when successful, ``False`` when failed. + + References + ---------- + + >>> oDefinitionManager.EditMaterial + + Examples + -------- + This example shows how to set a core loss model for a material in case material properties are calculated for + core losses at one frequency or core losses versus frequencies (core losses multicurve data). + The first case shows how to set properties for core losses at one frequency: + + >>> from pyaedt import Maxwell3d + >>> m3d = Maxwell3d() + >>> box = m3d.modeler.create_box([-10, -10, 0], [20, 20, 20], "box_to_split") + >>> box.material = "magnesium" + >>> m3d.materials["magnesium"].set_electrical_steel_coreloss( + ... points_list_at_freq={60 : [[0,0], [1,3.5], [2,7.4]]} + ... ) + >>> m3d.release_desktop(True, True) + + The second case shows how to set properties for core losses versus frequencies: + + >>> from pyaedt import Maxwell3d + >>> m3d = Maxwell3d() + >>> box = m3d.modeler.create_box([-10, -10, 0], [20, 20, 20], "box_to_split") + >>> box.material = "magnesium" + >>> m3d.materials["magnesium"].set_electrical_steel_coreloss( + ... points_list_at_freq={60 : [[0,0], [1,3.5], [2,7.4]], + ... 100 : [[0,0], [1,8], [2,9]], + ... 150 : [[0,0], [1,10], [2,19]]} + ... ) + >>> m3d.release_desktop(True, True) + + """ + if not isinstance(points_list_at_freq, dict): + raise TypeError("Points list at frequency must be provided as a dictionary.") + freq_keys = list(points_list_at_freq.keys()) + for i in range(0, len(freq_keys)): + if isinstance(freq_keys[i], str): + value, unit = decompose_variable_value(freq_keys[i]) + if unit != "Hz": + value = unit_converter(values=value, unit_system="Freq", input_units=unit, output_units="Hz") + points_list_at_freq[value] = points_list_at_freq[freq_keys[i]] + del points_list_at_freq[freq_keys[i]] + if "core_loss_type" not in self._props: + self._props["core_loss_type"] = OrderedDict( + {"property_type": "ChoiceProperty", "Choice": "Electrical Steel"} + ) + else: + self._props.pop("core_loss_cm", None) + self._props.pop("core_loss_x", None) + self._props.pop("core_loss_y", None) + self._props.pop("core_loss_hci", None) + self._props.pop("core_loss_br", None) + self._props.pop("core_loss_hkc", None) + self._props.pop("core_loss_curves", None) + self._props["core_loss_type"]["Choice"] = core_loss_model_type + if len(points_list_at_freq) == 1: + self._props["AttachedData"]["CoefficientSetupData"] = OrderedDict({}) + self._props["AttachedData"]["CoefficientSetupData"]["property_data"] = "coreloss_data" + self._props["AttachedData"]["CoefficientSetupData"]["coefficient_setup"] = coefficient_setup + frequency = list(points_list_at_freq.keys())[0] + self._props["AttachedData"]["CoefficientSetupData"]["Frequency"] = "{}Hz".format(frequency) + self._props["AttachedData"]["CoefficientSetupData"]["Thickness"] = thickness + self._props["AttachedData"]["CoefficientSetupData"]["Conductivity"] = str(conductivity) + points = [i for p in points_list_at_freq[frequency] for i in p] + self._props["AttachedData"]["CoefficientSetupData"]["Coordinates"] = OrderedDict( + {"DimUnits": ["", ""], "Points": points} + ) + elif len(points_list_at_freq) > 1: + self._props["AttachedData"]["CoreLossMultiCurveData"] = OrderedDict({}) + self._props["AttachedData"]["CoreLossMultiCurveData"]["property_data"] = "coreloss_multi_curve_data" + self._props["AttachedData"]["CoreLossMultiCurveData"]["coreloss_unit"] = "w_per_cubic_meter" + + self._props["AttachedData"]["CoreLossMultiCurveData"]["AllCurves"] = OrderedDict({}) + self._props["AttachedData"]["CoreLossMultiCurveData"]["AllCurves"]["OneCurve"] = [] + for freq in points_list_at_freq.keys(): + points = [i for p in points_list_at_freq[freq] for i in p] + one_curve = OrderedDict( + { + "Frequency": "{}Hz".format(freq), + "Coordinates": OrderedDict({"DimUnits": ["", ""], "Points": points}), + } + ) + self._props["AttachedData"]["CoreLossMultiCurveData"]["AllCurves"]["OneCurve"].append(one_curve) + + coefficients = self.get_core_loss_coefficients( + points_list_at_freq, thickness=thickness, conductivity=conductivity + ) + self._props["core_loss_kh"] = str(coefficients[0]) + self._props["core_loss_kc"] = str(coefficients[1]) + self._props["core_loss_ke"] = str(coefficients[2]) + self._props["core_loss_kdc"] = str(kdc) + self._props["core_loss_equiv_cut_depth"] = cut_depth + return self.update() + + @pyaedt_function_handler() + def set_electrical_steel_coreloss(self, kh=0, kc=0, ke=0, kdc=0, cut_depth="1mm"): + """Set electrical steel core loss. + + Parameters + ---------- + kh : float, optional + Hysteresis core loss coefficient. + The default is ``0``. + kc : float, optional + Eddy-current core loss coefficient. + The default is ``0``. + ke : float, optional + Excess core loss coefficient. + The default is ``0``. + kdc : float, optional + Coefficient considering the DC flux bias effects. + The default is ``0``. + cut_depth : str, optional + Equivalent cut depth considering manufacturing effects on core loss computation. + The default value is ``"1mm"``. Returns ------- @@ -2024,7 +2288,7 @@ def set_electrical_steel_coreloss(self, kh=0, kc=0, ke=0, kdc=0, cut_depth=0.000 self._props["core_loss_kc"] = str(kc) self._props["core_loss_ke"] = str(ke) self._props["core_loss_kdc"] = str(kdc) - self._props["core_loss_equiv_cut_depth"] = "{}meter".format(cut_depth) + self._props["core_loss_equiv_cut_depth"] = cut_depth return self.update() @pyaedt_function_handler() @@ -2306,9 +2570,9 @@ def set_djordjevic_sarkar_model( i_freq : int, float, optional. Input frequency in Hz. sigma_dc : int, float, optional - Conductivity at DC. + Conductivity at DC. The default is ``1e-12``. freq_hi : int, float, optional - High Frequency corner in Hz. + High-frequency corner in Hz. The default is ``159.15494e9``. Returns ------- @@ -2320,7 +2584,6 @@ def set_djordjevic_sarkar_model( K = "({} * {} - {} / (2 * pi * {} * e0)) / atan({} / {})".format(dk, df, sigma_dc, i_freq, freq_hi, i_freq) epsilon_inf = "({} - {} / 2 * ln({}**2 / {}**2 + 1))".format(dk, K, freq_hi, i_freq) freq_low = "({} / exp(10 * {} * {} / ({})))".format(freq_hi, df, epsilon_inf, K) - ds_er = "{} + {} / 2 * ln(({}**2 + Freq**2) / ({}**2 + Freq**2))".format(epsilon_inf, K, freq_hi, freq_low) cond = "{} + 2 * pi * Freq * e0 * ({}) * (atan(Freq / ({})) - atan(Freq / {}))".format( sigma_dc, K, freq_low, freq_hi From 2ba12b54d401c0a9bce7bbc1c141a73bc86bfb1e Mon Sep 17 00:00:00 2001 From: Zeba <85088388+zebanaqviWAT@users.noreply.github.com> Date: Fri, 24 Nov 2023 04:18:12 -0500 Subject: [PATCH 2/2] Adds methods to set band start and stop frequencies, & corresponding unit tests (#3773) * Adds methods to set band start and stop frequencies, and corresponding unit tests. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Docstring edits - parameter type for band_node, added examples * docstring edits, check for valid freq range, and start/stop frequency order. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update pyaedt/modeler/circuits/PrimitivesEmit.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update pyaedt/modeler/circuits/PrimitivesEmit.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * added more unit tests, docstring suggestions, removed Typeerror check * Update pyaedt/modeler/circuits/PrimitivesEmit.py * Update pyaedt/modeler/circuits/PrimitivesEmit.py * Update pyaedt/modeler/circuits/PrimitivesEmit.py * Update pyaedt/modeler/circuits/PrimitivesEmit.py * Update pyaedt/modeler/circuits/PrimitivesEmit.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com> Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> Co-authored-by: Samuel Lopez <85613111+Samuelopez-ansys@users.noreply.github.com> --- _unittest_solvers/test_26_emit.py | 39 ++++++ pyaedt/modeler/circuits/PrimitivesEmit.py | 138 ++++++++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/_unittest_solvers/test_26_emit.py b/_unittest_solvers/test_26_emit.py index c6a6ea6b6d9..31a24239686 100644 --- a/_unittest_solvers/test_26_emit.py +++ b/_unittest_solvers/test_26_emit.py @@ -206,6 +206,45 @@ def test_radio_component(self, add_app): assert start_freq == 0.1 start_freq = radio.band_start_frequency(band, "THz") assert start_freq == 0.0001 + # test band.set_band_start_frequency + start_freq = 10 + units = 'MHz' + radio.set_band_start_frequency(band, start_freq, units=units) + assert radio.band_start_frequency(band, units=units) == start_freq + start_freq = 20000000 + radio.set_band_start_frequency(band, start_freq) + assert radio.band_start_frequency(band, units='Hz') == start_freq + # test band.set_band_stop_frequency + stop_freq = 30 + units = 'MHz' + radio.set_band_stop_frequency(band, stop_freq, units=units) + assert radio.band_stop_frequency(band, units=units) == stop_freq + stop_freq = 40000000 + radio.set_band_stop_frequency(band, stop_freq) + assert radio.band_stop_frequency(band, units='Hz') == stop_freq + # test corner cases for band start and stop frequencies + start_freq = 10 + stop_freq = 9 + units = 'Hz' + radio.set_band_start_frequency(band, start_freq, units=units) + radio.set_band_stop_frequency(band, stop_freq, units=units) + assert radio.band_start_frequency(band, units="Hz") == 8 + radio.set_band_start_frequency(band, 10, units=units) + assert radio.band_stop_frequency(band, units="Hz") == 11 + units = 'wrong' + radio.set_band_stop_frequency(band, 10, units=units) + assert radio.band_stop_frequency(band, units='Hz') == 10 + radio.set_band_start_frequency(band, 10, units=units) + assert radio.band_start_frequency(band, units='Hz') == 10 + with pytest.raises(ValueError) as e: + start_freq = 101 + units = 'GHz' + radio.set_band_start_frequency(band, start_freq, units=units) + assert "Frequency should be within 1Hz to 100 GHz." in str(e) + stop_freq = 102 + radio.set_band_stop_frequency(band, stop_freq, units=units) + assert "Frequency should be within 1Hz to 100 GHz." in str(e) + # test power unit conversions band_power = radio.band_tx_power(band) assert band_power == 40.0 diff --git a/pyaedt/modeler/circuits/PrimitivesEmit.py b/pyaedt/modeler/circuits/PrimitivesEmit.py index a9843eb5426..98b1c68c137 100644 --- a/pyaedt/modeler/circuits/PrimitivesEmit.py +++ b/pyaedt/modeler/circuits/PrimitivesEmit.py @@ -744,6 +744,144 @@ def band_stop_frequency(self, band_node, units=""): units = self.units["Frequency"] return consts.unit_converter(float(band_node.props["StopFrequency"]), "Freq", "Hz", units) + def set_band_start_frequency(self, band_node, band_start_freq, units=""): + """Set start frequency of the band. + + Parameters + ---------- + band_node : pyaedt.modeler.circuits.PrimitivesEmit.EmitComponentPropNode object + Instance of the band node + band_start_freq : float + Start frequency of the band. + units : str, optional + Units of the start frequency. If no units are specified or the units + specified are invalid, then `"Hz"`` is assumed. + + Returns + ------ + None + + Examples + -------- + >>> from pyaedt import Emit + >>> aedtapp = Emit(new_desktop_session=False) + >>> radio = aedtapp.modeler.components.create_component("New Radio") + >>> band = radio.bands()[0] + >>> start_freq = 10 + >>> units = 'MHz' + >>> radio.set_band_start_frequency(band, start_freq, units=units) + """ + + # if "Band" not in band_node.props["Type"]: + # raise TypeError("{} must be a band.".format(band_node.node_name)) + + if not units or units not in emit_consts.EMIT_VALID_UNITS["Frequency"]: + units = "Hz" + + # convert to Hz + freq_float_in_Hz = consts.unit_converter(band_start_freq, "Freq", units, "Hz") + freq_string = "{}".format(freq_float_in_Hz) + if not (1 <= freq_float_in_Hz <= 100000000000): # pragma: no cover + raise ValueError("Frequency should be within 1Hz to 100 GHz.") + if float(band_node.props["StopFrequency"]) < freq_float_in_Hz: # pragma: no cover + stop_freq = freq_float_in_Hz + 1 + band_node._set_prop_value({"StopFrequency": str(stop_freq)}) + else: + prop_list = {"StartFrequency": freq_string} + band_node._set_prop_value(prop_list) + + def set_band_stop_frequency(self, band_node, band_stop_freq, units=""): + """Set stop frequency of the band. + + Parameters + ---------- + band_node : pyaedt.modeler.circuits.PrimitivesEmit.EmitComponentPropNode object + Instance of the band node + band_stop_freq : float + Stop frequency of the band. + units : str, optional + Units of the stop frequency. If no units are specified or the units + specified are invalid, then `"Hz"`` is assumed. + + Returns + ------ + None + + Examples + -------- + >>> from pyaedt import Emit + >>> aedtapp = Emit(new_desktop_session=False) + >>> radio = aedtapp.modeler.components.create_component("New Radio") + >>> band = radio.bands()[0] + >>> stop_freq = 10 + >>> units = 'MHz' + >>> radio.set_band_stop_frequency(band, stop_freq, units=units) + """ + # if "Band" not in band_node.props["Type"]: + # raise TypeError("{} must be a band.".format(band_node.node_name)) + if not units or units not in emit_consts.EMIT_VALID_UNITS["Frequency"]: + units = "Hz" + # convert to Hz + freq_float_in_Hz = consts.unit_converter(band_stop_freq, "Freq", units, "Hz") + if not (1 <= freq_float_in_Hz <= 100000000000): # pragma: no cover + raise ValueError("Frequency should be within 1Hz to 100 GHz.") + if float(band_node.props["StartFrequency"]) > freq_float_in_Hz: # pragma: no cover + if freq_float_in_Hz > 1: + band_node._set_prop_value({"StartFrequency": str(freq_float_in_Hz - 1)}) + else: # pragma: no cover + raise ValueError("Band stop frequency is less than start frequency.") + freq_string = "{}".format(freq_float_in_Hz) + prop_list = {"StopFrequency": freq_string} + band_node._set_prop_value(prop_list) + + # def duplicate_band(self, band_node_to_duplicate): + # """ + # [Incomplete 10/19/2023] + # Parameters + # ---------- + # band_node_to_duplicate + # + # Returns + # ------- + # + # """ + # # append number to the name of the band to duplicate. + # print('Duplicating...') + # + # + # # return band node + # def convert_channels_to_multi_bands(self, band_node): + # """ + # [Incomplete 10/19/2023] + # Parameters + # ---------- + # band_node + # + # Returns + # ------- + # + # """ + # # get the channels. Say returns 10 channels in the band_node + # # Name = r.bands()[0].children[0].props['Name'] + # # band_node.props['Name'] + # # Start = r.bands()[0].props['StartFrequency'] + # band_start_frequency = float(band_node.props['StartFrequency']) + # # Stop = r.bands()[0].props['StopFrequency'] + # band_stop_frequency = float(band_node.props['StopFrequency']) + # # Spacing = r.bands()[0].props['ChannelSpacing'] + # channel_spacing = float(band_node.props['ChannelSpacing']) + # # for each channel + # # 1) create a band (duplicate original one) + # # 2) set band start and stop frequencies + # for channel in list(range(int(band_start_frequency), int(band_stop_frequency), int(channel_spacing))): + # baby_band_start = channel + # baby_band_stop = channel+channel_spacing + # baby_band_node = self.duplicate_band(band_node) # return band name or some handle to it + # self.set_band_start_frequency(baby_band_node, baby_band_start) + # self.set_band_stop_frequency(baby_band_node, baby_band_stop) + # # set start and stop freq for that band name + # # to be + def band_channel_bandwidth(self, band_node, units=""): """Get the channel bandwidth of the band node.