-
Notifications
You must be signed in to change notification settings - Fork 3
Adaptive Templates Corner Rounding Example - e10 template different corner radii #465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c3cf99e
a2b1218
0a46d43
9e7cb33
69b1985
e81b2f8
d4e0f8e
a4c4bd4
d500fab
57d88ca
6c47066
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,310 @@ | ||
| # Copyright (C) 2022 - 2025 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. | ||
|
|
||
| """ | ||
| Round Rotor Lamination and Magnet Corners | ||
| ========================================= | ||
| This script applies the adaptive templates functionality to modify IPM rotor lamination and magnet | ||
| regions from having sharp corners to round corners. | ||
| """ | ||
| # %% | ||
| # With Motor-CAD standard template geometry, we can define corner rounding for Rotor Lamination and | ||
| # Magnet regions, however the same radius is used for all magnet corners, and the same radius is | ||
| # used for all rotor lamination corners. | ||
| # | ||
| # With the Motor-CAD Adaptive Templates functionality, we can use the ``Region.round_corners`` | ||
| # method to separately round regions. For example, for an IPM motor, we can define a one corner | ||
| # radius for layer 1 magnets, and a different corner radius for layer 2 magnets. | ||
| # | ||
| # This Adaptive Templates script sets up corner rounding for the e10 Motor-CAD template file, | ||
| # defining separate corner radii for magnets in layers 1 and 2, and separate corner radii for the | ||
| # rotor lamination pocket regions for the two magnet layers. | ||
|
|
||
| # %% | ||
| # .. image:: ../../images/adaptive_templates/IPM_rounded_3.png | ||
| # :width: 600pt | ||
|
|
||
| # %% | ||
| # This script is designed to be run from Motor-CAD template "e10". If no Motor-CAD file is open, the | ||
| # e10 template will be loaded. | ||
| # | ||
| # This script uses the following adaptive parameters: | ||
| # | ||
| # * L1 Rotor Lam Corner Radius (0.9) | ||
| # | ||
| # * L1 Magnet Corner Radius (1.5) | ||
| # | ||
| # * L2 Rotor Lam Corner Radius (0.5) | ||
| # | ||
| # * L2 Magnet Corner Radius (1) | ||
| # | ||
| # If these parameters are not already set up in the Motor-CAD file, the parameters will be | ||
| # automatically set, with the default values shown in brackets. | ||
|
|
||
| # %% | ||
| # Perform required imports | ||
| # ------------------------ | ||
| # Import ``pymotorcad`` to access Motor-CAD. | ||
| # Import ``deepcopy`` to define the adaptive template geometry. | ||
| # Import ``os``, ``shutil``, ``sys``, and ``tempfile`` | ||
| # to open and save a temporary .mot file if none is open. | ||
|
|
||
| from copy import deepcopy | ||
|
|
||
| # sphinx_gallery_thumbnail_path = 'images/adaptive_templates/IPM_rounded.png' | ||
| import os | ||
| import shutil | ||
| import sys | ||
| import tempfile | ||
|
|
||
| import ansys.motorcad.core as pymotorcad | ||
|
|
||
| # %% | ||
| # 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: a new Motor-CAD instance is opened, the e10 | ||
| # IPM motor template is loaded, the default **Corner Rounding (Rotor Lamination)** and | ||
| # **Corner Rounding (Magnets)** is set to **None (default)** and the file is saved 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. | ||
| 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) | ||
| mc.set_visible(True) | ||
| mc.load_template("e10") | ||
| mc.set_variable("CornerRounding_Rotor", 0) | ||
| mc.set_variable("CornerRounding_Magnets", 0) | ||
|
|
||
| # 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 = "e10_IPM_Round_Rotor_Lam_and_Magnets" | ||
| mc.save_to_file(working_folder + "/" + mot_name + ".mot") | ||
|
|
||
| # Reset geometry to default | ||
| mc.reset_adaptive_geometry() | ||
|
|
||
|
|
||
| # %% | ||
| # Define necessary functions | ||
| # -------------------------- | ||
| # Corners to round | ||
| # ~~~~~~~~~~~~~~~~ | ||
| # For a region that shares an entity with another region, create a list of corner coordinates to | ||
| # be rounded. Points that are shared by both regions are not to be rounded. | ||
| # | ||
| # For example: if the region to round is the rotor pocket shown below, where a line is shared with | ||
| # a magnet region, then a list of the points circled in green will be returned. The points circled | ||
| # in red (shared with the magnet region) are not returned by the function. | ||
|
|
||
| # %% | ||
| # .. image:: ../../images/adaptive_templates/IPM_rounded_1.png | ||
| # :width: 600pt | ||
|
|
||
|
|
||
| def corners_to_round(region_to_round, other_regions): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a comment about what this does. I think it's finding the points in region_to_round that aren't in other_regions, but it would be useful to explain this. |
||
| corner_list = [] | ||
| other_region_points = [] | ||
| if type(other_regions) == list: | ||
| for other_region in other_regions: | ||
| other_region_points.extend(other_region.points) | ||
| else: | ||
| other_region_points.extend(other_regions.points) | ||
|
|
||
| for point in region_to_round.points: | ||
| if point not in other_region_points: | ||
| corner_list.append(point) | ||
| return corner_list | ||
|
|
||
|
|
||
| # %% | ||
| # Get magnet cutout | ||
| # ~~~~~~~~~~~~~~~~~ | ||
| # For a corresponding magnet and rotor pocket region, get a region that represents the entire region | ||
| # cutout of the rotor lamination. | ||
| # | ||
| # For example, for the rotor pocket and magnet regions, the entire region cutout is shown below. | ||
|
|
||
|
|
||
| # %% | ||
| # .. image:: ../../images/adaptive_templates/IPM_rounded_2.png | ||
| # :width: 600pt | ||
| def get_magnet_cutout(magnets, pocket): | ||
| # copy pocket region to create magnet cut out region | ||
| magnet_cut_out = deepcopy(pocket) | ||
| # Keep pocket properties but replace pocket entities with magnet entities | ||
| if type(magnets) == list: | ||
| magnets_utd = deepcopy(magnets[0]) | ||
| magnets_utd.unite(magnets[1]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is assuming that if magnets is a list, it contains exactly 2 items. Is this always true? A comment to explain the logic here might help |
||
| magnet_cut_out.replace(magnets_utd) | ||
| else: | ||
| magnet_cut_out.replace(magnets) | ||
| # unite magnet cut out and original pocket to form entire magnet cut out region | ||
| try: | ||
| magnet_cut_out.unite(pocket) | ||
| except: | ||
| pocket.unite(magnets) | ||
| magnet_cut_out.update(pocket) | ||
| return magnet_cut_out | ||
|
|
||
|
|
||
| # %% | ||
| # Round magnet pockets | ||
| # ~~~~~~~~~~~~~~~~~~~~ | ||
| # For a given magnet region and entire rotor pocket region (with magnet cutout), round the | ||
| # corresponding rotor pocket region using the ``Region.round_corners`` method and the | ||
| # ``corners_to_round`` function defined above. | ||
| def round_magnet_pockets(magnets, full_pocket, rotor_lam_corner_radius): | ||
| if type(magnets) == list: | ||
| for magnet in magnets: | ||
| full_pocket.subtract(magnet) | ||
| else: | ||
| full_pocket.subtract(magnets) | ||
|
|
||
| full_pocket.round_corners(corners_to_round(full_pocket, magnets), rotor_lam_corner_radius) | ||
|
|
||
|
|
||
| # %% | ||
| # Round magnet and pockets | ||
| # ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| # For a given magnet region: | ||
| # | ||
| # * Get the corresponding rotor pocket regions. | ||
| # | ||
| # * Get the entire rotor pocket region using the ``get_magnet_cutout`` function defined above. | ||
| # | ||
| # * Round the magnet region corners using the ``Region.round_corners`` method. | ||
| # | ||
| # * Round the rotor pocket region corners using the ``round_magnet_pockets`` function defined above. | ||
| def round_magnet_and_pockets( | ||
| magnet_region, rotor_region, magnet_corner_radius, rotor_lam_corner_radius | ||
| ): | ||
| # get corresponding pocket regions | ||
| pockets = [] | ||
| for region in rotor_region.children: | ||
| if "Pocket" in region.name: | ||
| pocket_region = region | ||
| for point in pocket_region.points: | ||
| if point in magnet_region.points: | ||
| pockets.append(pocket_region) | ||
| break | ||
|
|
||
| # get entire pocket | ||
| for index in range(len(pockets)): | ||
| pockets[index] = get_magnet_cutout(magnet_region, pockets[index]) | ||
|
|
||
| # round magnet | ||
| magnet_region.round_corners(magnet_region.points, magnet_corner_radius) | ||
| mc.set_region(magnet_region) | ||
|
|
||
| # round pockets | ||
| for index_1 in range(len(pockets)): | ||
| if index_1 > 0: | ||
| for index_2 in range(index_1): | ||
| pockets[index_1].subtract(pockets[index_2]) | ||
| round_magnet_pockets(magnet_region, pockets[index_1], rotor_lam_corner_radius) | ||
| mc.set_region(pockets[index_1]) | ||
|
|
||
|
|
||
| # %% | ||
| # 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 | ||
| # ``L1 Rotor Lam Corner Radius``, ``L1 Magnet Corner Radius``, ``L2 Rotor Lam Corner Radius`` and | ||
| # ``L2 Magnet Corner Radius`` parameters if undefined. | ||
|
|
||
| mc.set_adaptive_parameter_default("L1 Rotor Lam Corner Radius", 0.9) | ||
| mc.set_adaptive_parameter_default("L1 Magnet Corner Radius", 1.5) | ||
| mc.set_adaptive_parameter_default("L2 Rotor Lam Corner Radius", 0.5) | ||
| mc.set_adaptive_parameter_default("L2 Magnet Corner Radius", 1) | ||
|
|
||
|
|
||
| # %% | ||
| # Get the corner radius adaptive parameter values. | ||
| L1_rotor_lam_corner_radius = mc.get_adaptive_parameter_value("L1 Rotor Lam Corner Radius") | ||
| L1_magnet_corner_radius = mc.get_adaptive_parameter_value("L1 Magnet Corner Radius") | ||
| L2_rotor_lam_corner_radius = mc.get_adaptive_parameter_value("L2 Rotor Lam Corner Radius") | ||
| L2_magnet_corner_radius = mc.get_adaptive_parameter_value("L2 Magnet Corner Radius") | ||
|
|
||
| # %% | ||
| # Get the necessary standard template regions. These are the magnet regions with corners to be | ||
| # rounded, and the rotor region. Rotor pocket regions that correspond to the magnets will be | ||
| # obtained from the list of rotor child regions. The regions can be drawn for debugging if | ||
| # required. | ||
|
|
||
| rotor = mc.get_region("Rotor") | ||
|
|
||
| L1_1Magnet2 = mc.get_region("L1_1Magnet2") | ||
| L1_1Magnet1 = mc.get_region("L1_1Magnet1") | ||
| L2_1Magnet2 = mc.get_region("L2_1Magnet2") | ||
| L2_1Magnet1 = mc.get_region("L2_1Magnet1") | ||
|
|
||
| # %% | ||
| # Create the Adaptive Templates geometry | ||
| # -------------------------------------- | ||
| # Use the ``round_magnet_and_pockets`` function to round the magnet regions and their corresponding | ||
| # rotor pocket regions. | ||
| round_magnet_and_pockets(L1_1Magnet2, rotor, L1_magnet_corner_radius, L1_rotor_lam_corner_radius) | ||
JackB-Ansys marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| round_magnet_and_pockets(L1_1Magnet1, rotor, L1_magnet_corner_radius, L1_rotor_lam_corner_radius) | ||
|
|
||
| round_magnet_and_pockets(L2_1Magnet2, rotor, L2_magnet_corner_radius, L2_rotor_lam_corner_radius) | ||
| round_magnet_and_pockets(L2_1Magnet1, rotor, L2_magnet_corner_radius, L2_rotor_lam_corner_radius) | ||
|
|
||
|
|
||
| # %% | ||
| # .. image:: ../../images/adaptive_templates/IPM_rounded.png | ||
| # :width: 600pt | ||
|
|
||
| # %% | ||
| # 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. | ||
|
|
||
| # %% | ||
| # .. 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. | ||
| 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") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the sphinx image path should be in the middle of the imports, can it be moved above or below please?