{
"cells": [
{
"cell_type": "markdown",
"id": "42b320a4",
"metadata": {},
"source": [
"# Internal permanent magnet e-machine\n",
"\n",
"This example shows how to run a parametric analysis of\n",
"an electric machine and report torque and loss over the\n",
"range of swept parameters.\n",
"\n",
"The model applies electrical symmetry\n",
"and neglects end-winding 3D effects, thereby allowing analysis of one\n",
"quarter of a\n",
"2D cross-section. This common approach greatly \n",
"accelerates the analysis relative to a full 3D analysis. The\n",
"model cross-section is shown below.\n",
"\n",
"
\n",
"\n",
"Keywords: **Maxwell 2D**, **transient**, **motor**"
]
},
{
"cell_type": "markdown",
"id": "2db52dca",
"metadata": {},
"source": [
"## Prerequisites\n",
"\n",
"### Perform imports"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78e38b38",
"metadata": {},
"outputs": [],
"source": [
"import csv\n",
"import os\n",
"import tempfile\n",
"import time\n",
"\n",
"import ansys.aedt.core\n",
"from ansys.aedt.core.examples.downloads import download_file"
]
},
{
"cell_type": "markdown",
"id": "49a4c970",
"metadata": {},
"source": [
"### Define constants\n",
"Constants help ensure consistency and avoid repetition throughout the example."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d545090e",
"metadata": {},
"outputs": [],
"source": [
"AEDT_VERSION = \"2025.2\"\n",
"NUM_CORES = 4\n",
"NG_MODE = False # Open AEDT UI when it is launched."
]
},
{
"cell_type": "markdown",
"id": "4630a4ad",
"metadata": {},
"source": [
"### Create temporary directory\n",
"\n",
"Create a temporary working directory.\n",
"The name of the working folder is stored in ``temp_folder.name``.\n",
"\n",
"> **Note:** The final cell in the notebook cleans up the temporary folder. If you want to\n",
"> retrieve the AEDT project and data, do so before executing the final cell in the notebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2cf0adec",
"metadata": {},
"outputs": [],
"source": [
"temp_folder = tempfile.TemporaryDirectory(suffix=\".ansys\")"
]
},
{
"cell_type": "markdown",
"id": "4e017755",
"metadata": {},
"source": [
"## Model preparation\n",
"\n",
"### Download the example project file\n",
"\n",
"Many PyAEDT examples use project files or data from the\n",
"[Ansys example data repository](https://www.github.com/ansys/example-data) \n",
"in GitHub.\n",
"\n",
"> *Note:* You can update ``settings`` as shown below\n",
"> to work with a local copy of the\n",
"> [example-data](https://github.com/ansys/example-data) repository.\n",
"> Replace ``'/home/user/repo/example-data'`` with\n",
"> the path to the cloned ``\"example-data\"`` repository.\n",
"\n",
"``` python\n",
"from ansys.aedt.core import settings\n",
"settings.use_example_data = True\n",
"settings.local_example_folder = r'/home/user/repo/example-data'\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "6ba91596",
"metadata": {
"lines_to_next_cell": 2
},
"source": [
"Retrieve the example model and place\n",
"it in the project folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1ce5acc4",
"metadata": {},
"outputs": [],
"source": [
"aedt_file = download_file(\n",
" source=\"maxwell_motor_optimization\",\n",
" name=\"IPM_optimization.aedt\",\n",
" local_path=temp_folder.name,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e873e690",
"metadata": {},
"source": [
"### Launch Maxwell 2D\n",
"\n",
"Launch AEDT and Maxwell 2D after first setting up the project, the version and the graphical mode."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "90a42b71",
"metadata": {},
"outputs": [],
"source": [
"m2d = ansys.aedt.core.Maxwell2d(\n",
" project=aedt_file,\n",
" version=AEDT_VERSION,\n",
" new_desktop=True,\n",
" non_graphical=NG_MODE,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "dea9272d",
"metadata": {},
"source": [
"### Define parameters\n",
"\n",
"Permanent magnet material may be used as a degree of freedom for\n",
"the optimization. The material is modified in maxwell using the parameter\n",
"``\"mat_index\"``\n",
"\n",
"| mat_index | Material |\n",
"| :----: | :----: |\n",
"| 0 | NdFeB
(\"XG196/96_2DSF1.000_X\") |\n",
"| 1| NdFe30 |\n",
"| 2 | NdFe35 |"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "637584ee",
"metadata": {},
"outputs": [],
"source": [
"m2d[\"mat_sweep\"] = '[\"XG196/96_2DSF1.000_X\", \"NdFe30\", \"NdFe35\"]'\n",
"m2d[\"mat_index\"] = 0"
]
},
{
"cell_type": "markdown",
"id": "1e505d2b",
"metadata": {},
"source": [
"### Assign the material definition to the permanent magnets\n",
"\n",
"Retrieve all permanent magnet objects using the string \"Mag\" for\n",
"the object name and assign the material definition \n",
"``\"mat_sweep[mat_index]\"``. This allows the material assignment to\n",
"be modified by changing the value of ``mat_index`` in Maxwell 2D."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7dc2e326",
"metadata": {},
"outputs": [],
"source": [
"magnets = [o for name, o in m2d.modeler.objects_by_name.items() \n",
" if \"Mag\" in name]\n",
"\n",
"for mag in magnets:\n",
" mag.material_name = \"mat_sweep[mat_index]\""
]
},
{
"cell_type": "markdown",
"id": "13aa1a1d",
"metadata": {},
"source": [
"### Specify parametric analysis\n",
"\n",
"The following code demonstrates how to set up a parametric study that varies:\n",
"- ``\"din\"``: Stator inner diameter.\n",
"- ``\"mat_index\"``: Material of the permanent magnet.\n",
"\n",
"Variations can be added\n",
"using the ``add_variation()`` method to extend the\n",
"scope of the parametric investigation. This approach provides a powerful\n",
"interface for advanced design of experiments and optimization."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "187b0afc",
"metadata": {},
"outputs": [],
"source": [
"param_sweep = m2d.parametrics.add( # Define parametric analysis.\n",
" variable=\"bridge\", # Single value for \"bridge\"\n",
" start_point=\"0.5mm\",\n",
" variation_type=\"SingleValue\",\n",
")\n",
"param_sweep.add_variation( # Sweep \"din\".\n",
" sweep_variable=\"din\",\n",
" start_point=78,\n",
" end_point=80,\n",
" step=10,\n",
" units=\"mm\",\n",
" variation_type=\"LinearStep\",\n",
")\n",
"param_sweep.add_variation( # Single value for \"phase_advance\".\n",
" sweep_variable=\"phase_advance\",\n",
" start_point=45,\n",
" units=\"deg\",\n",
" variation_type=\"SingleValue\",\n",
")\n",
"param_sweep.add_variation( # Single value for \"Ipeak\".\n",
" sweep_variable=\"Ipeak\", \n",
" start_point=200, \n",
" units=\"A\", \n",
" variation_type=\"SingleValue\"\n",
")\n",
"param_sweep.add_variation( # Sweep PM material assignment.\n",
" sweep_variable=\"mat_index\",\n",
" start_point=0,\n",
" end_point=2,\n",
" step=1,\n",
" variation_type=\"LinearStep\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "0649530b",
"metadata": {},
"source": [
"#### CSV file import for parametric analysis\n",
"The parametric analysis created\n",
"above could also be defined from a\n",
"table of comma-separated values. The header of the CSV file\n",
"contains the parameter names as shown below.\n",
"\n",
"*CSV file content:*\n",
"```\n",
"*, mat_index, bridge, Ipeak, phase_adv, din\n",
"1, 0, 0.5mm, 200A, 45deg, 78mm\n",
"2, 1, 0.5mm, 200A, 45deg, 78mm\n",
"3, 2, 0.5mm, 200A, 45deg, 78mm\n",
"4, 0, 0.5mm, 200A, 45deg, 80mm\n",
"5, 1, 0.5mm, 200A, 45deg, 80mm\n",
"6, 2, 0.5mm, 200A, 45deg, 80mm\n",
"```\n",
"Table of parametric variations:\n",
"\n",
"| Simulation
ID | mat_index | bridge | Ipeak | phase_adv | din | \n",
"| :---: | :----: | :----: | :----: | :----: | :----: |\n",
"| 1 | 0 | 0.5mm | 200 A | 45 ° | 78 mm |\n",
"| 2 | 1 | 0.5mm | 200 A | 45 ° | 78 mm |\n",
"| 3 | 2 | 0.5mm | 200 A | 45 ° | 78 mm |\n",
"| 4 | 0 | 0.5mm | 200 A | 45 ° | 80 mm |\n",
"| 5 | 1 | 0.5mm | 200 A | 45 ° | 80 mm |\n",
"| 6 | 2 | 0.5mm | 200 A | 45 ° | 80 mm |\n",
"\n",
"The parametric analysis can be imported using the following method:\n",
"``` python\n",
"m2d.parametrics.add_from_file(\"/path/to/file.csv\")\n",
"```\n",
"\n",
"### Run parametric analysis"
]
},
{
"cell_type": "markdown",
"id": "f30950a0",
"metadata": {},
"source": [
"To speed up the analysis, the time step is increased in the transient setup.\n",
"This can be done by modifying the ``TimeStep`` property of the transient setup.\n",
"Note: In a real case scenario, the time step should be: ``1/freq_e/360``.\n",
"To simulate a real case scenario, please comment out the following line."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "586dddbc",
"metadata": {},
"outputs": [],
"source": [
"m2d.setups[0].props[\"TimeStep\"] = \"1/freq_e/45\"\n",
"param_sweep.analyze(cores=NUM_CORES)"
]
},
{
"cell_type": "markdown",
"id": "0a4bfcf0",
"metadata": {},
"source": [
"## Postprocess\n",
"\n",
"Create reports to view torque and loss.\n",
"\n",
"Plot torque for all PM materials with ``din`` set to 78mm."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5374a472",
"metadata": {},
"outputs": [],
"source": [
"report_torque_constant_din = m2d.post.create_report(\n",
" expressions=\"Moving1.Torque\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"78mm\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"torque_constant_din\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "64afed35",
"metadata": {},
"source": [
"Plot torque vs ``\"din\"`` with the PM material\n",
"set to NdFeB (``\"XG196/96_2DSF1.000_X\"``)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9908c16d",
"metadata": {},
"outputs": [],
"source": [
"report_torque_constant_mat= m2d.post.create_report(\n",
" expressions=\"Moving1.Torque\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"0\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"torque_constant_mat\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "32325012",
"metadata": {},
"source": [
"The same approach is applied to visualize the loss in all solids and \n",
"the core. Quantities ``\"SolidLoss\"`` and ``\"CoreLoss\"`` are calculated\n",
"from the field solution and were pre-defined in the project."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "99225e02",
"metadata": {},
"outputs": [],
"source": [
"report_solid_loss_constant_din = m2d.post.create_report(\n",
" expressions=\"SolidLoss\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"78mm\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"solid_loss_constant_din\",\n",
")\n",
"\n",
"report_solid_loss_constant_mat = m2d.post.create_report(\n",
" expressions=\"SolidLoss\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"0\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"solid_loss_constant_mat\",\n",
")\n",
"\n",
"report_core_loss_constant_din = m2d.post.create_report(\n",
" expressions=\"CoreLoss\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"78mm\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"core_loss_constant_din\",\n",
")\n",
"\n",
"report_core_loss_constant_mat = m2d.post.create_report(\n",
" expressions=\"CoreLoss\",\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"0\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" plot_type=\"Rectangular Plot\",\n",
" plot_name=\"core_loss_constant_mat\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a39e9ec9",
"metadata": {},
"source": [
"Report the torque and loss for all variations."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "133dc1dc",
"metadata": {},
"outputs": [],
"source": [
"torque_data = m2d.post.get_solution_data(\n",
" expressions=[\"Moving1.Torque\"],\n",
" setup_sweep_name=m2d.nominal_sweep,\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" report_category=\"Standard\",\n",
")\n",
"\n",
"solid_loss_data = m2d.post.get_solution_data(\n",
" expressions=[\"SolidLoss\"],\n",
" setup_sweep_name=m2d.nominal_sweep,\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" report_category=\"Standard\",\n",
")\n",
"\n",
"core_loss_data = m2d.post.get_solution_data(\n",
" expressions=[\"CoreLoss\"],\n",
" setup_sweep_name=m2d.nominal_sweep,\n",
" domain=\"Sweep\",\n",
" variations={\n",
" \"bridge\": \"All\",\n",
" \"din\": \"All\",\n",
" \"Ipeak\": \"All\",\n",
" \"phase_advance\": \"All\",\n",
" \"mat_index\": \"All\",\n",
" },\n",
" primary_sweep_variable=\"Time\",\n",
" report_category=\"Standard\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "c280fc69",
"metadata": {},
"source": [
"Calculate the time-averaged torque and loss for each\n",
"variation and export the data to a .csv file."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15839bb3",
"metadata": {},
"outputs": [],
"source": [
"csv_data = []\n",
"for var in core_loss_data.variations:\n",
" torque_data.active_variation = var\n",
" core_loss_data.active_variation = var\n",
" solid_loss_data.active_variation = var\n",
"\n",
" torque_values = torque_data.get_expression_data(formula=\"magnitude\")[1]\n",
" core_loss_values = core_loss_data.get_expression_data(formula=\"magnitude\")[1]\n",
" solid_loss_values = solid_loss_data.get_expression_data(formula=\"magnitude\")[1]\n",
"\n",
" torque_data_average = sum(torque_values) / len(torque_values)\n",
" core_loss_average = sum(core_loss_values) / len(core_loss_values)\n",
" solid_loss_average = sum(solid_loss_values) / len(solid_loss_values)\n",
"\n",
" csv_data.append(\n",
" {\n",
" \"active_variation\": str(torque_data.active_variation),\n",
" \"average_torque\": str(torque_data_average),\n",
" \"average_core_loss\": str(core_loss_average),\n",
" \"average_solid_loss\": str(solid_loss_average),\n",
" }\n",
" )\n",
"\n",
" with open(\n",
" os.path.join(temp_folder.name, \"motor_optimization.csv\"), \"w\", newline=\"\"\n",
" ) as csvfile:\n",
" fields = [\n",
" \"active_variation\",\n",
" \"average_torque\",\n",
" \"average_core_loss\",\n",
" \"average_solid_loss\",\n",
" ]\n",
" writer = csv.DictWriter(csvfile, fieldnames=fields)\n",
" writer.writeheader()\n",
" writer.writerows(csv_data)"
]
},
{
"cell_type": "markdown",
"id": "49cdc1ef",
"metadata": {},
"source": [
"## Finish\n",
"\n",
"### Save the project"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7f2e4568",
"metadata": {},
"outputs": [],
"source": [
"m2d.save_project()\n",
"m2d.release_desktop()\n",
"# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.\n",
"time.sleep(3)"
]
},
{
"cell_type": "markdown",
"id": "3afc608f",
"metadata": {},
"source": [
"## Clean up\n",
"\n",
"All project files are saved in the folder ``temp_folder.name``.\n",
"If you've run this example as a Jupyter notebook, you\n",
"can retrieve those project files. The following cell\n",
"removes all temporary files, including the project folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "26aa3c04",
"metadata": {},
"outputs": [],
"source": [
"temp_folder.cleanup()"
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"main_language": "python",
"notebook_metadata_filter": "-all"
}
},
"nbformat": 4,
"nbformat_minor": 5
}