.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples\adaptive_library\AsymmetricSPM.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_adaptive_library_AsymmetricSPM.py: Asymmetric Surface Permanent Magnet Rotor ========================================= This script applies the adaptive templates functionality to modify a 28 pole surface permanent magnet rotor to have an asymmetric arrangement of magnets. .. GENERATED FROM PYTHON SOURCE LINES 30-44 .. note:: This example uses Motor-CAD Geometry Tree functionality, introduced in v2026.1.1 (Motor-CAD 2026 R1) and PyMotorCAD v0.8.4 or later. .. note:: This example modifies the symmetry of the rotor region. By default, Motor-CAD standard template geometry uses 1 rotor region per magnet pole. This example modifies the symmetry so that the model geometry has 4 rotor regions, each spanning a quarter of the machine. This means that the **Symmetry** setting under **Model Size** on the **Input Data -> Settings -> Calculation** tab in Motor-CAD must be set to **Full Non-Symmetry** for the FEA calculation to solve. Each magnet position is shifted by rotating by an offset angle. The offset angle parameters are defined as new adaptive templates parameters. .. GENERATED FROM PYTHON SOURCE LINES 47-49 .. image:: ../../images/adaptive_templates/asymmetric_SPM_2.png :width: 800pt .. GENERATED FROM PYTHON SOURCE LINES 51-60 Perform required imports ------------------------ Import ``pymotorcad`` to access Motor-CAD. Import ``RegionType``, ``Line``, ``Arc``, ``Coordinate``, ``rt_to_xy`` and ``EntityList`` for creating the adaptive templates geometry. Import ``draw_objects`` to plot figures of geometry objects. Import ``deepcopy`` to copy geometry objects. Import ``os``, ``shutil``, ``sys``, and ``tempfile`` to open and save a temporary .mot file if none is open. .. GENERATED FROM PYTHON SOURCE LINES 60-75 .. code-block:: Python # from copy import deepcopy import os import shutil import sys import tempfile import ansys.motorcad.core as pymotorcad from ansys.motorcad.core.geometry import Arc, Coordinate, EntityList, Line, RegionType, rt_to_xy from ansys.motorcad.core.geometry_drawing import draw_objects # from ansys.motorcad.core.geometry_drawing import draw_objects # import ansys.motorcad.core.geometry_tree as geo_tree .. GENERATED FROM PYTHON SOURCE LINES 77-88 Connect to Motor-CAD -------------------- If this script is loaded into the Adaptive Templates file in Motor-CAD, the current Motor-CAD instance is used. If the script is run externally, these actions occur: open a new Motor-CAD instance, load the a2 SPM motor template, set the **Symmetry** setting under **Model Size** to **Full Non-Symmetry** (**Input Data -> Settings -> Calculation** tab), set the **Magnet Arc [ED]** to **100 °** and save the file to a temporary folder. To keep a new Motor-CAD instance open after executing the script, use the ``MotorCAD(keep_instance_open=True)`` option when opening the new instance. Alternatively, use the ``MotorCAD()`` method, which closes the Motor-CAD instance after the script is executed. .. GENERATED FROM PYTHON SOURCE LINES 88-116 .. code-block:: Python if pymotorcad.is_running_in_internal_scripting(): # Use existing Motor-CAD instance if possible mc = pymotorcad.MotorCAD(open_new_instance=False) else: mc = pymotorcad.MotorCAD(keep_instance_open=True) # Disable popup messages mc.set_variable("MessageDisplayState", 2) if not "PYMOTORCAD_DOCS_BUILD" in os.environ: mc.set_visible(True) mc.load_template("a2") mc.set_variable("MagneticSymmetry", 3) # set Full Non-Symmetry mc.set_variable("Magnet_Arc_[ED]", 100) # Open relevant file working_folder = os.path.join(tempfile.gettempdir(), "adaptive_library") try: shutil.rmtree(working_folder) except: pass os.mkdir(working_folder) mot_name = "a2_Asymmetric_SPM" mc.save_to_file(working_folder + "/" + mot_name + ".mot") # Reset geometry to default mc.reset_adaptive_geometry() .. GENERATED FROM PYTHON SOURCE LINES 117-123 Get required parameters and objects ----------------------------------- From Motor-CAD, get the adaptive parameters and their values. Use the ``set_adaptive_parameter_default`` method to set the required symmetry factor (``Symmetry Factor``) parameter if undefined, and get the value. .. GENERATED FROM PYTHON SOURCE LINES 125-127 .. image:: ../../images/adaptive_templates/asymmetric_SPM_1.png :width: 800pt .. GENERATED FROM PYTHON SOURCE LINES 129-131 In this example, the 28 pole rotor is split into quarters, with each quarter containing 7 magnets. The default symmetry factor value is set to 7. .. GENERATED FROM PYTHON SOURCE LINES 131-135 .. code-block:: Python mc.set_adaptive_parameter_default("Symmetry factor", 7) symmetry_factor = int(mc.get_adaptive_parameter_value("Symmetry factor")) .. GENERATED FROM PYTHON SOURCE LINES 136-138 This example sets an offset angle for each of the 7 magnets, which define how many degrees each magnet will be shifted by. .. GENERATED FROM PYTHON SOURCE LINES 140-142 .. image:: ../../images/adaptive_templates/asymmetric_SPM_3.png :width: 600pt .. GENERATED FROM PYTHON SOURCE LINES 144-164 Define some default values for the offset angles. Include a check for the case where the ``symmetry factor`` is set to a higher number than the length of the ``offset_angle_default_values`` list. In this case, set values of 0 ° for any extra angles. Define the following angles for the 7 magnets, going anti-clockwise from the magnet closest to the x-axis: * -2 ° * +4 ° * 0 ° (this magnet is not offset) * -4 ° * +2 ° * -2 ° * +2 ° .. GENERATED FROM PYTHON SOURCE LINES 164-170 .. code-block:: Python offset_angle_default_values = [-2, 4, 0, -4, 2, -2, 2] extra_values_req = symmetry_factor - len(offset_angle_default_values) if extra_values_req > 0: for i in range(extra_values_req): offset_angle_default_values.append(0) .. GENERATED FROM PYTHON SOURCE LINES 171-174 Set the required asymmetric magnet offset angle (``Magnet Offset Angle x``, where x is the magnet number) parameters if undefined, and get the values. Append the offset angle parameters to the ``offset_angles`` list. .. GENERATED FROM PYTHON SOURCE LINES 174-181 .. code-block:: Python offset_angles = [] for i in range(symmetry_factor): mc.set_adaptive_parameter_default( f"Rotor Pole Offset Angle {i+1}", offset_angle_default_values[i] ) offset_angles.append(mc.get_adaptive_parameter_value(f"Rotor Pole Offset Angle {i+1}")) .. GENERATED FROM PYTHON SOURCE LINES 182-185 Get the standard template regions to be modified. This example works using the geometry tree functionality. Get the geometry tree, then get the standard template regions based on the region type. .. GENERATED FROM PYTHON SOURCE LINES 185-189 .. code-block:: Python gt = mc.get_geometry_tree() magnets = gt.get_regions_of_type(RegionType.magnet) rotor_air = gt.get_regions_of_type(RegionType.rotor_air) .. GENERATED FROM PYTHON SOURCE LINES 190-192 Create the Adaptive Templates geometry -------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 194-206 Modify symmetry of original magnet and air regions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example works by changing the number of times that geometry regions are duplicated. By default, a 28 pole rotor geometry is set up by defining the geometry for one rotor pole, and then duplicating this 28 times to form the full motor. In this example, we will define a new magnet geometry for 4 consecutive rotor poles, and this 4-pole magnet geometry will be duplicated 7 times to form the full motor. Instead of the original 1 magnet per pole that is defined for the standard template geometry, 4 magnets will be defined. Modify the duplication angles of the original magnet and rotor air regions. .. GENERATED FROM PYTHON SOURCE LINES 206-211 .. code-block:: Python for magnet in magnets: magnet.duplications = int(magnet.duplications / symmetry_factor) for air_region in rotor_air: air_region.duplications = int(air_region.duplications / symmetry_factor) .. GENERATED FROM PYTHON SOURCE LINES 212-220 Create full air band region ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The rotor air regions will be recreated based on the new magnet positions. To do so, define the full rotor air region. Magnet regions will be subtracted from this to form the multiple new rotor air regions. Find the rotor air outer and inner radii (``rad_max`` and ``rad_min``). Use the ``Coordinate.get_polar_coords_deg()`` method to return the polar coordinates of point coordinates. .. GENERATED FROM PYTHON SOURCE LINES 220-244 .. code-block:: Python rad_max = 0 rad_min = 10000 for entity in rotor_air[0].entities: rad, __ = entity.end.get_polar_coords_deg() if rad > rad_max: rad_max = rad elif rad < rad_min: rad_min = rad # Create the entities for drawing the full rotor air band before it is split into individual # regions. Use the ``rt_to_xy`` from ``ansys.motorcad.core.geometry`` to convert from polar # to cartesian coordinates. p1 = Coordinate(*rt_to_xy(rad_min, 0)) p2 = Coordinate(*rt_to_xy(rad_max, 0)) p3 = Coordinate(*rt_to_xy(rad_max, 360 / rotor_air[0].duplications)) p4 = Coordinate(*rt_to_xy(rad_min, 360 / rotor_air[0].duplications)) full_rotor_air_entities = EntityList() full_rotor_air_entities.append(Line(p1, p2)) full_rotor_air_entities.append(Arc(p2, p3, centre=Coordinate(0, 0))) full_rotor_air_entities.append(Line(p3, p4)) full_rotor_air_entities.append(Arc(p4, p1, centre=Coordinate(0, 0))) draw_objects(full_rotor_air_entities) .. image-sg:: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_001.png :alt: AsymmetricSPM :srcset: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 245-274 Duplicate and rotate the original magnet region ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create copies of the original magnet region and rotate the magnets based on the 'offset_angles'. For each additional magnet: * Use the ``create_region`` method to create a new region in the ``gt`` geometry tree. The ``create_region`` method requires ``region_type`` and ``parent`` parameters. * Set the ``region_type=RegionType.magnet`` to create a magnet region, and set the ``parent`` to the original magnet region's parent. * Set the material, colour and symmetry properties of the new magnet region. * Give the new magnet region a name. * Use the ``replace`` method to replace the new magnet region's entities with those of the original magnet, then rotate the region around the origin. The rotation angle can be calculated from the duplication number (symmetry) and adaptive parameters (symmetry factor and the offset angle for the particular magnet). * Calculate and set the new magnet angle, which defines the direction in which the magnet is polarised. * For even numbered magnets, set the polarity to ``S`` and switch the magnetisation to the opposite direction by subtracting 180 ° from the ``magnet_angle``. For odd numbered magnets, set the polarity to ``N``. * Append all magnets to the ``new_magnets`` list. .. GENERATED FROM PYTHON SOURCE LINES 274-297 .. code-block:: Python new_magnets = [] for magnet in magnets: new_magnets.append(magnet) for i in range(symmetry_factor - 1): new_magnet = gt.create_region(region_type=RegionType.magnet, parent=magnet.parent) new_magnet.material = magnet.material new_magnet.colour = magnet.colour new_magnet.duplications = magnet.duplications new_magnet.name = f"{magnet.name}_{i + 1}Offset" new_magnet.replace(magnet) rot_angle = ((i + 1) * 360 / (magnet.duplications * symmetry_factor)) + offset_angles[i + 1] new_magnet.rotate(Coordinate(0, 0), rot_angle) new_magnet.magnet_angle = magnet.magnet_angle + rot_angle if i % 2 == 0: new_magnet.magnet_polarity = "S" new_magnet.magnet_angle -= 180 else: new_magnet.magnet_polarity = "N" new_magnets.append(new_magnet) .. GENERATED FROM PYTHON SOURCE LINES 298-300 Rotate the original magnet by its offset angle (if non-zero). Do this last so that the original magnet's position could be used for calculating the new magnet positions. Draw the magnet regions. .. GENERATED FROM PYTHON SOURCE LINES 300-306 .. code-block:: Python if offset_angles[0] != 0: new_magnets[0].rotate(Coordinate(0, 0), offset_angles[0]) new_magnets[0].magnet_angle += offset_angles[0] draw_objects(new_magnets) .. image-sg:: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_002.png :alt: AsymmetricSPM :srcset: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 307-313 Subtract regions from rotor air band to get new air regions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add the full rotor air region to the geometry tree using the ``create_region`` geometry tree method. Specify the ``RegionType`` (rotor air) and the parent region of the rotor air region (the same as the parent of the original rotor air region). Set the duplication number and set the entities to the ``full_rotor_air_entities`` list that was defined earlier. .. GENERATED FROM PYTHON SOURCE LINES 313-317 .. code-block:: Python full_rotor_air = gt.create_region(RegionType.rotor_air, rotor_air[0].parent) full_rotor_air.duplications = rotor_air[0].duplications full_rotor_air.entities = full_rotor_air_entities .. GENERATED FROM PYTHON SOURCE LINES 318-323 Subtract the magnet regions from the full rotor air region to get the separate rotor air regions that will sit between the magnets. Use the ``subtract`` region method, which returns a list of additional regions in the case where subtracting a magnet from the air region results in more than one region. Name the additional air regions and store them in the ``all_extras`` list. Draw the rotor air regions. .. GENERATED FROM PYTHON SOURCE LINES 323-337 .. code-block:: Python all_extras = [] i = 0 for magnet in new_magnets: extras = full_rotor_air.subtract(magnet) for extra in extras: extra.name = f"RotorAir_{i}" all_extras.extend(extras) i += 1 to_draw = [full_rotor_air] to_draw.extend(all_extras) draw_objects(to_draw) .. image-sg:: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_003.png :alt: AsymmetricSPM :srcset: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 338-340 The original air region must also be renamed. It is no longer the full rotor air region. It is now the last individual rotor air region going clockwise from the x-axis. .. GENERATED FROM PYTHON SOURCE LINES 340-342 .. code-block:: Python full_rotor_air.name = f"RotorAir_{i}" .. GENERATED FROM PYTHON SOURCE LINES 343-345 Now that the new rotor air regions have been created, remove the original ``rotor_air`` regions from the geometry tree. .. GENERATED FROM PYTHON SOURCE LINES 345-348 .. code-block:: Python for region in rotor_air: gt.remove_region(region) .. GENERATED FROM PYTHON SOURCE LINES 349-358 So far, only the last air region ``full_rotor_air`` has been added to the geometry tree. Add the other rotor air regions (stored in the ``all_extras`` list) to the geometry tree using the ``create_region`` geometry tree method. For each new rotor air ``TreeRegion``: create the new ``TreeRegion``, set the name, entities and duplication number. Link the first and last rotor air regions (going clockwise from the x-axis). These are the regions either side of the symmetry boundary, and will form a continuous region in the full motor model. Use the ``linked_region`` method to set the linked region of the first rotor air region to be the ``full_rotor_air`` region (the last rotor air region). .. GENERATED FROM PYTHON SOURCE LINES 358-368 .. code-block:: Python i = 0 for region in all_extras: new_air_region = gt.create_region(full_rotor_air.region_type, full_rotor_air.parent) new_air_region.name = region.name new_air_region.entities = region.entities new_air_region.duplications = full_rotor_air.duplications if i == 0: new_air_region.linked_region = full_rotor_air i += 1 .. GENERATED FROM PYTHON SOURCE LINES 369-372 Set the modified geometry tree in Motor-CAD ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set the geometry tree and draw the geometry tree. .. GENERATED FROM PYTHON SOURCE LINES 372-376 .. code-block:: Python mc.set_geometry_tree(gt) draw_objects(gt, full_geometry=True, axes=False) .. image-sg:: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_004.png :alt: AsymmetricSPM :srcset: /examples/adaptive_library/images/sphx_glr_AsymmetricSPM_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 377-387 Load in Adaptive Templates script if required --------------------------------------------- When this script is run externally, the script executes the following: * Set **Geometry type** to **Adaptive**. * Load the script into the **Adaptive Templates** tab. * Go to the **Geometry -> Radial** tab to run the Adaptive Templates script and display the new geometry. .. GENERATED FROM PYTHON SOURCE LINES 389-392 .. note:: When running in a Jupyter Notebook, you must provide the path for the Adaptive Templates script (PY file) instead of ``sys.argv[0]`` when using the ``load_adaptive_script()`` method. .. GENERATED FROM PYTHON SOURCE LINES 392-396 .. code-block:: Python if not pymotorcad.is_running_in_internal_scripting(): mc.set_variable("GeometryTemplateType", 1) mc.load_adaptive_script(sys.argv[0]) mc.display_screen("Geometry;Radial") .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 50.652 seconds) .. _sphx_glr_download_examples_adaptive_library_AsymmetricSPM.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: AsymmetricSPM.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: AsymmetricSPM.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: AsymmetricSPM.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_