diff --git a/examples/basics/emag_basics.py b/examples/basics/emag_basics.py index 6322fb03e..bdb57a679 100644 --- a/examples/basics/emag_basics.py +++ b/examples/basics/emag_basics.py @@ -25,209 +25,236 @@ Motor-CAD E-magnetic example script =================================== -This example provides a Motor-CAD E-magnetic script. This script -creates a partial custom winding pattern to change -parameter values, run the analysis, and plot results. To create -a full winding pattern, parameters must be specified -for all coils. +This example provides a Motor-CAD E-magnetic script to run calculations and plot results. """ - # %% +# This script will change model parameter values (geometry, winding pattern and materials), +# run magnetic calculations, extract and plot results. +# +# .. note:: +# This script creates a partial custom winding pattern. To create a full winding pattern, +# parameters must be specified for all coils. +# # Set up example # -------------- -# Setting up this example consists of performing imports, specifying the -# working directory, launching Motor-CAD, and disabling all popup -# messages from Motor-CAD. +# To set up the example, perform imports, specify the working directory, launch Motor-CAD, and +# disable all popup messages from Motor-CAD. # # Perform required imports # ~~~~~~~~~~~~~~~~~~~~~~~~ +# Import ``pymotorcad`` to access Motor-CAD. Import ``pyplot`` from ``matplotlib`` to display +# graphics. Import ``os``, ``shutil`` and ``tempfile`` to open and save a temporary MOT +# file if none is open. - +# sphinx_gallery_thumbnail_number = -2 import os +import shutil +import tempfile import matplotlib.pyplot as plt import ansys.motorcad.core as pymotorcad -if "QT_API" in os.environ: - os.environ["QT_API"] = "pyqt" - # %% # Specify working directory # ~~~~~~~~~~~~~~~~~~~~~~~~~ - -working_folder = os.getcwd() - -if os.path.isdir(working_folder) is False: - print("Working folder does not exist. Choose a folder that exists and try again.") - print(working_folder) - exit() +# Save the file to a temporary folder +working_folder = os.path.join(tempfile.gettempdir(), "basic_examples") +try: + shutil.rmtree(working_folder) +except: + pass +os.mkdir(working_folder) +mot_name = "EMagnetic" # %% # Launch Motor-CAD # ~~~~~~~~~~~~~~~~ - +# Connect to Motor-CAD and open a new Motor-CAD instance. 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. print("Starting initialization.") -mcad = pymotorcad.MotorCAD() +mc = pymotorcad.MotorCAD() # %% # Disable popup messages # ~~~~~~~~~~~~~~~~~~~~~~ - -mcad.set_variable("MessageDisplayState", 2) -print("Initialization completed.") -print("Running simulation.") - -# %% -# Create analysis -# --------------- -# Creating the analysis consists of showing the magnetic context, displaying -# the **Scripting** tab, setting the geometry and parameters, and saving -# the file. +# Disable all popup messages from Motor-CAD by setting the ``MessageDisplayState`` parameter to 2. # -# Show the magnetic context. -mcad.show_magnetic_context() +# .. note:: +# This will also disable crucial popups, for example prompts to save data or overwrite data, or +# dialogs used to reconcile differences in material data between the database and MOT file. In +# each case the default action will be taken. This setting will persist until Motor-CAD is +# closed. +mc.set_variable("MessageDisplayState", 2) # %% -# Display the **Scripting** tab. -mcad.display_screen("Scripting") +# Set Motor-CAD to EMag +# ~~~~~~~~~~~~~~~~~~~~~ +# Show the E-Magnetic context in Motor-CAD and ensure that the BPM +# Motor Type is set. +mc.show_magnetic_context() +mc.set_variable("Motor_Type", 0) # %% -# Set the geometry. -mcad.set_variable("Slot_Number", 24) -mcad.set_variable("Tooth_Width", 6) -mcad.set_variable("Magnet_Thickness", 4.5) +# Set up analysis +# --------------- +# Setting up the analysis consists of setting the geometry, winding and material parameters, setting +# the calculation options and operating point, and saving the file. +# +# Geometry setup +# ~~~~~~~~~~~~~~ +# Display the **Scripting** tab and set the ``Slot_Number``, ``Tooth_Width`` and +# ``Magnet_Thickness`` geometry parameters. +# +# .. note:: +# To set parameter values via automation with the GUI visible, a different tab must be displayed. +# The **Scripting** tab is chosen because no parameters on this tab will be changed in this +# script. It is best practice to display the **Scripting** tab when changing input parameters by +# automation. This is required only if the GUI is shown. +mc.display_screen("Scripting") +mc.set_variable("Slot_Number", 24) +mc.set_variable("Tooth_Width", 6) +mc.set_variable("Magnet_Thickness", 4.5) # %% -# Set parameters for creating the custom winding pattern. +# Winding setup +# ~~~~~~~~~~~~~~ +# Instead of using the automatic winding pattern, set parameters for creating a partial custom +# winding pattern. This example sets up an unevenly distributed winding pattern: # -# The following code creates only a partial winding pattern. +# * Set the winding type to *Custom*. # -# Set the winding type to custom: -# :code:`mcad.set_variable('MagWindingType', 1)` +# * Set the path type to *Upper/Lower*. # -# Set the path type to upper and lower: -# :code:`mcad.set_variable('MagPathType', 1)` +# * Set the number of phases to 3. # -# Set the number of phases: -# :code:`mcad.set_variable('MagPhases', 3)` +# * Set the number of parallel paths to 1. # -# Set the number of parallel paths: -# :code:`mcad.set_variable('ParallelPaths', 1)` +# * Set the number of winding layers to 2. # -# Set the number of winding layers: -# :code:`mcad.set_variable('WindingLayers', 2)` +# * Define parameters for coil 3 using the ``set_winding_coil()`` method. Set positions to a, b, c +# etc. when using Upper/Lower paths (when Left/Right paths are selected, L or R should be used +# instead). Define the third coil to have 60 turns in the second phase. +mc.set_variable("MagneticWindingType", 2) +mc.set_variable("MagPathType", 1) +mc.set_variable("MagPhases", 3) +mc.set_variable("ParallelPaths", 1) +mc.set_variable("WindingLayers", 2) +mc.set_winding_coil(2, 1, 3, 4, "b", 18, "a", 60) + +# %% +# the changes to the winding pattern are shown below # -# Define a coil's parameters: -# :code:`set_winding_coil(phase, -# path, coil, go_slot, go_position, return_slot, return_position, turns)` +# .. image:: ../../images/basics_emag_2.png # %% -# Set the stator/rotor lamination materials. -mcad.set_component_material("Stator Lam (Back Iron)", "M250-35A") -mcad.set_component_material("Rotor Lam (Back Iron)", "M250-35A") +# Materials setup +# ~~~~~~~~~~~~~~~ +# Set the stator and rotor lamination materials. Component names are displayed in the +# *Materials* tab in Motor-CAD. Materials and their properties are defined in the Motor-CAD +# *Material database*. +mc.set_component_material("Stator Lam (Back Iron)", "M250-35A") +mc.set_component_material("Rotor Lam (Back Iron)", "M250-35A") # %% -# Set the torque calculation options. -points_per_cycle = 30 -number_cycles = 1 -mcad.set_variable("TorquePointsPerCycle", points_per_cycle) -mcad.set_variable("TorqueNumberCycles", number_cycles) +# Calculation options +# ~~~~~~~~~~~~~~~~~~~ +# Set the torque calculation options for a transient torque calculation with 30 points in 1 +# electrical cycle. +mc.set_variable("TorquePointsPerCycle", 30) +mc.set_variable("TorqueNumberCycles", 1) # %% # Disable all performance tests except the ones for transient torque. -mcad.set_variable("BackEMFCalculation", False) -mcad.set_variable("CoggingTorqueCalculation", False) -mcad.set_variable("ElectromagneticForcesCalc_OC", False) -mcad.set_variable("TorqueSpeedCalculation", False) -mcad.set_variable("DemagnetizationCalc", False) -mcad.set_variable("ElectromagneticForcesCalc_Load", False) -mcad.set_variable("InductanceCalc", False) -mcad.set_variable("BPMShortCircuitCalc", False) +mc.set_variable("BackEMFCalculation", False) +mc.set_variable("CoggingTorqueCalculation", False) +mc.set_variable("ElectromagneticForcesCalc_OC", False) +mc.set_variable("TorqueSpeedCalculation", False) +mc.set_variable("DemagnetizationCalc", False) +mc.set_variable("ElectromagneticForcesCalc_Load", False) +mc.set_variable("InductanceCalc", False) +mc.set_variable("BPMShortCircuitCalc", False) # %% -# Enable transient torque. -mcad.set_variable("TorqueCalculation", True) +# Enable transient torque calculation. +mc.set_variable("TorqueCalculation", True) # %% -# Set the operating point. -mcad.set_variable("Shaft_Speed_[RPM]", 1000) -mcad.set_variable("CurrentDefinition", 0) -mcad.set_variable("PeakCurrent", 3) -mcad.set_variable("DCBusVoltage", 350) -mcad.set_variable("PhaseAdvance", 45) +# Operating point +# ~~~~~~~~~~~~~~~ +# Set the operating point of the machine, defined by the speed, current and phase advance. Ensure +# that the current definition is set to *Peak* (index 0 of the radio group option). +mc.set_variable("Shaft_Speed_[RPM]", 1000) +mc.set_variable("CurrentDefinition", 0) +mc.set_variable("PeakCurrent", 3) +mc.set_variable("DCBusVoltage", 350) +mc.set_variable("PhaseAdvance", 45) # %% -# Save the file. -filename = os.path.join(working_folder, "../ActiveX_Scripting_EMagnetic.mot") -mcad.save_to_file(filename) +# Save the file +# ~~~~~~~~~~~~~ +# Save the file to the temporary folder and display a message that the initialisation in complete. +mc.save_to_file(working_folder + "/" + mot_name + ".mot") +print("Initialisation completed.") # %% # Run simulation # -------------- -# Run the simulation. -mcad.do_magnetic_calculation() +# Run the electromagnetic calculation in Motor-CAD (solve the e-magnetic model). +print("Running simulation.") +mc.do_magnetic_calculation() # %% # Export results to CSV file # -------------------------- -# Export results to a CSV file. -exportFile = os.path.join(working_folder, "../Export_EMag_Results.csv") +# Export results to a CSV file. Results will be saved as *Export_EMag_Results.csv* in the temporary +# working directory. Use a try and except block to raise an error in the case of the export failing. +# +# Use the ``MotorCADError`` object to obtain error messages from Motor-CAD and raise an Exception in +# the Python script. try: - mcad.export_results("EMagnetic", exportFile) + mc.export_results("EMagnetic", working_folder + "/Export_EMag_Results.csv") print("Results successfully exported.") -except pymotorcad.MotorCADError: - print("Results failed to export.") +except pymotorcad.MotorCADError as e: + print("Results failed to export due to Motor-CAD Error: " + str(e)) # %% -# Get and analyze results +# Get and analyse results # ----------------------- -# Get torque and voltage data. -shaft_torque = mcad.get_variable("ShaftTorque") -line_voltage = mcad.get_variable("PeakLineLineVoltage") +# Retrieve the torque and voltage output data values. +shaft_torque = mc.get_variable("ShaftTorque") +line_voltage = mc.get_variable("PeakLineLineVoltage") +print( + f"Shaft Torque: {shaft_torque:.2f} Nm\n" + f"Line-Line Terminal Voltage (peak): {line_voltage:.2f} V" +) # %% -# Graph the torque data. -num_torque_points = points_per_cycle * number_cycles -rotor_position = [] -torque_vw = [] - -for n in range(num_torque_points): - (x, y) = mcad.get_magnetic_graph_point("TorqueVW", n) - rotor_position.append(x) - torque_vw.append(y) - -# %% -# Graph the airgap flux density data. -loop = 0 -success = 0 -mech_angle = [] -airgap_flux_density = [] +# Retrieve the *Torque (VW)* graph data. The data type for this series is *E-Magnetics*, so use the +# ``get_magnetic_graph()`` method. +# +# .. note:: +# When retrieving graph data points, use the **Graph Viewer** under **Help** in the Motor-CAD +# interface to find the series names and data types. +rotor_position, torque_vw = mc.get_magnetic_graph("TorqueVW") # %% -# Keep looking until you cannot find the point. -while success == 0: - try: - (x, y) = mcad.get_fea_graph_point("B Gap (on load)", 1, loop, 0) - mech_angle.append(x) - airgap_flux_density.append(y) - loop = loop + 1 - except pymotorcad.MotorCADError: - success = 1 +# Retrieve the airgap flux density graph data. This data can be found in the *Graph Viewer* in +# Motor-CAD. The data type for this series is **FEA Path**, so use the ``get_fea_graph()`` method. +mech_angle, airgap_flux_density = mc.get_fea_graph("B Gap (on load)", 1, 0) # %% -# Graph the harmonic data. -num_harmonic_points = (points_per_cycle * number_cycles) + 1 -data_point = [] -torque = [] -for n in range(num_harmonic_points): - try: - (x, y) = mcad.get_magnetic_graph_point("HarmonicDataCycle", n) - data_point.append(x) - torque.append(y) - except pymotorcad.MotorCADError: - print("Results failed to export.") +# Only the most recently displayed harmonic graphs are available in Motor-CAD via the +# **Graph Viewer**. Retrieve the torque harmonic graph data using the +# ``get_magnetic_graph_harmonics()`` method. +# +# .. note:: +# The ``get_magnetic_graph_harmonics()`` method is available from PyMotorCAD v0.7.2 onwards. The +# previous workflow for extracting harmonic graph data is detailed in the PyMotorCAD v0.6 +# documentation. +harmonic_order, harmonic_amplitude, __ = mc.get_magnetic_graph_harmonics("TorqueVW") print("Simulation completed.") @@ -235,7 +262,8 @@ # %% # Plot results # ------------ -# Plot results from the simulation. +# Use``pyplot`` from ``matplotlib`` (imported as ``plt``) to plot graphs of the simulation results. +# Plot the Airgap Flux Density and the Torque (VW) results as subplots in the same figure. plt.subplot(211) plt.plot(mech_angle, airgap_flux_density) plt.xlabel("Mech Angle") @@ -244,19 +272,19 @@ plt.plot(rotor_position, torque_vw) plt.xlabel("Rotor Position") plt.ylabel("TorqueVW") -plt.figure(2) -plt.plot(data_point, torque) -plt.xlabel("DataPoint") -plt.ylabel("Torque (Nm)") plt.show() # %% -# Exit Motor-CAD -# -------------- -# Exit Motor-CAD. -mcad.quit() +# Plot the Torque harmonic results as a bar chart. +plt.figure(2) +plt.bar(harmonic_order, harmonic_amplitude) +plt.xlabel("Harmonic order (Electrical)") +plt.ylabel("Harmonic amplitude (Nm)") +plt.show() # %% -# If you want to continue working with this instance of Motor-CAD, rather -# than using the preceding command, use this command: -# :code:`mcad.set_variable('MessageDisplayState', 0)` +# If you want to continue working with this instance of Motor-CAD, use the +# ``MotorCAD(keep_instance_open=True)`` option when you launch Motor-CAD. If Motor-CAD is kept open, +# it is useful to restore the popup messages that were disabled earlier, using the +# ``set_variable()`` method. +mc.set_variable("MessageDisplayState", 0) diff --git a/src/ansys/motorcad/core/methods/rpc_methods_graphs.py b/src/ansys/motorcad/core/methods/rpc_methods_graphs.py index a185a4fab..6a6f928a6 100644 --- a/src/ansys/motorcad/core/methods/rpc_methods_graphs.py +++ b/src/ansys/motorcad/core/methods/rpc_methods_graphs.py @@ -216,8 +216,8 @@ def get_fea_graph_point(self, graph_id, slice_number, point_number, time_step_nu point_number : int Point number to get x and y coordinate values from. - time_step_number - + time_step_number : int + Time step number to get x and y coordinate values from. Returns ------- xValue : float