{ "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 }