Geometry Tutorial 2: (Almost) Creating a Rotor Template

In this tutorial, you will almost learn how to create a geometry template for the rotor of a simple switched reluctance motor (SRM). (Indeed, the geometry will be fully usable, just not encapsulated inside its own Matlab class).

Along the journey, we will also learn some other useful items:

Contents

Initializing dimensions

We begin by collecting the important dimensions inside a Matlab struct. Why? This allows us to collect all rotor-related dimensions under a single Matlab variable. Furthermore, turning this script into a function later is then much easier.

Now, let's skip to the business and set some dimensions:

dimensions = struct();
dimensions.Rout = 50e-3; %outer diameter of our rotor
dimensions.Qr = 6; %number of rotor teeth
dimensions.w_tooth_r_out = 18e-3; %tooth tip width
dimensions.w_tooth_r_in = 21e-3; %tooth width near rotor yoke
dimensions.h_tooth_r = 20e-3; %tooth height

As can be seen, our simple rotor geometry is fully determined by only a few dimensions:

Creating the tooth tip

We begin by creating the tip of the rotor tooth. First, let us assume we have a single rotor tooth laying centered on the x-axis. Next, we create a Point to represented the counter-clockwise corner of the tooth tip:

pout_clockwise = Point([sqrt(dimensions.Rout^2-(dimensions.w_tooth_r_out/2)^2), ...
    -dimensions.w_tooth_r_out/2], 1e-3);

Note how the x-coordinate of the Point is not exactly dimensions.Rout. Instead, it's somewhat smaller according to the Pythagorean theorem to maintain the correct airgap radius:

norm(pout_clockwise) - dimensions.Rout
ans =

     0

However, the recommended way of creating rotor pole shapes for EMDtool is not to have the pole d-axis centered along the x-axis.

Instead, the geometry should be rotated by one half of the pole pitch.

Let us do that now for the point we just created, and plot it before and after the rotation:

figure(1); clf; hold on;  box on; axis equal;
pout_clockwise.plot('pout-clockwise', 'ko'); %plot before rotation

%rotation:
pout_clockwise = pout_clockwise.rotate( pi/dimensions.Qr );
%pout_clockwise.rotate_inplace(  pi/dimensions.Qr ); %this would also work

pout_clockwise.plot('pout-clockwise after rotation', 'mo'); %plot after rotation

snapnow;

Finally, to create the counter-clockwise equivalent of the tooth-tip corner point, we use the mirror function of the Point class:

pout_counterclockwise = pout_clockwise.mirror( 2*pi/dimensions.Qr );

Like stated in the documentation, this method mirrors the point around the center of the segment with a width of 2*pi/dimensions.Qr radians. For reference, let's plot both the original point and the mirrored one, and the segment centerline:

clf; hold on; box on;
set(gca, 'XLim', [0 dimensions.Rout*1.5]); daspect([1 1 1]);
pout_clockwise.plot('Original point', 'mo'); %plot after rotation
pout_counterclockwise.plot('Mirrored point', 'ro');

plot([0 dimensions.Rout*cos(pi/dimensions.Qr)], [0 dimensions.Rout*sin(pi/dimensions.Qr)], 'k--');

Creating the tooth body and rotor core

Next, let's create the points for the tooth bottom, and the rotor core.

r_in = dimensions.Rout - dimensions.h_tooth_r; %radial coordinate of tooth bottom

pin_clockwise = Point([sqrt(r_in^2-(dimensions.w_tooth_r_in/2)^2), ...
    -dimensions.w_tooth_r_in/2], 5e-3).rotate( pi/dimensions.Qr );
pin_counterclockwise = pin_clockwise.mirror( 2*pi/dimensions.Qr );

p_core_cw = Point( [r_in, 0], 10e-3);
p_core_ccw = p_core_cw.mirror( 2*pi/dimensions.Qr );

Finally, let's create a Surface object.

O = Point([0,0], 10e-3);

s_core = Surface('core', ...
    geo.line, O, p_core_cw, 'n_cw', ...
    geo.arc, p_core_cw, O, pin_clockwise, ...
    geo.line, pin_clockwise, pout_clockwise, ...
    geo.arc, pout_clockwise, O, pout_counterclockwise, 'n_ag', ...
    geo.line, pout_counterclockwise, pin_counterclockwise, ...
    geo.arc, pin_counterclockwise, O, p_core_ccw, ...
    geo.line, p_core_ccw, O, 'n_ccw');

we set the boundary lines as periodic, to allow mesh replication and periodic boundary conditions:

geo.set_periodic(O, p_core_cw, O, p_core_ccw);

Visualize the surface and points for clarity:

s_core.plot('k');

O.plot('Origin', 'ko');
pin_clockwise.plot('pin-clockwise', 'ko');
pin_counterclockwise.plot('pin-counterclockwise', 'ko');
p_core_cw.plot('p-core-cw', 'ms');
p_core_ccw.plot('p-core-ccw', 'ms');

Now, note that while defining the Surface, we used quite a many named Curves:

We'll see later that by defining these Curves, with these exact names, many boring and error-prone tasks can be handled automatically:

UPDATE (3.0.1): geo.set_periodic method now does the naming (n_cw, n_ccw) automatically.

Creating a Geometry object

Next, we demonstrate several classes used for creating and analysing finite element models:

We begin by first defining some parameters necessary for mesh replication

dimensions.Nrep = 3; %we are not replicating the mesh here
dimensions.sector_angle = 2*pi/dimensions.Qr;

and then initializing the RadialGeometry object:

rotor = RadialGeometry(dimensions) %Geometry objects
rotor = 

  RadialGeometry with properties:

          mesh: []
             p: []
             t: []
         n_dir: []
          n_cw: []
         n_ccw: []
          n_ag: []
         edges: [1×1 struct]
    boundaries: [1×1 struct]
      circuits: []
          data: [1×1 struct]
       domains: []
     materials: []
           PMs: []
          name: ''
    dimensions: [1×1 struct]
        parent: []

As can be seen, the class has quite a few interesting properties like:

Next, we create a Material object for the rotor iron.

Iron = Material.create(4);

Similarly, we create Domain object for representing the rotor core.

Core = Domain('Core', Iron, s_core);

Despite their apparent similarity, Domain objects are entirely different from Surface objects. Indeed, Surface objects

By contrast, Domain objects

Next, we add the materials and domains to the geometry:

rotor.add_material(Iron);
rotor.add_domain(Core);

Meshing the geometry object

Finally, it is time to mesh the geometry.

rotor.mesh_elementary_geometry();
clf; hold on; box on; axis equal;
rotor.triplot('k');

Next, we replicate the mesh to cover the entire symmetry sector:

rotor.replicate_elementary_mesh( )

clf; hold on; box on; axis equal;
rotor.triplot('k');

Adding polegap domains

Obviously, our rotor model is not yet complete. Indeed, we need to add some air between the rotor tooth.

This follows exactly the same approach as before, beginning with defining the desired Points and Surfaces.

p_air_cw = Point([dimensions.Rout, 0], 1e-3);
p_air_ccw = p_air_cw.mirror( 2*pi/dimensions.Qr );

s_air_cw = Surface('air', ...
    geo.line, p_core_cw, p_air_cw, 'n_cw', ...
    geo.arc, p_air_cw, O, pout_clockwise, 'n_ag', ...
    geo.line, pout_clockwise, pin_clockwise, ...
    geo.arc, pin_clockwise, O,  p_core_cw);

s_air_ccw = Surface('air', ...
    geo.line, p_core_ccw, p_air_ccw, 'n_ccw', ...
    geo.arc, p_air_ccw, O, pout_counterclockwise, 'n_ag', ...
    geo.line, pout_counterclockwise, pin_counterclockwise, ...
    geo.arc, pin_counterclockwise, O,  p_core_ccw);

geo.set_periodic(p_core_cw, p_air_cw, p_core_ccw, p_air_ccw);

clf; hold on; box on; axis equal;
s_core.plot('k');
s_air_cw.plot('r', 'linewidth',2);
s_air_ccw.plot('r', 'linewidth',2);

Next, it is turn to add the corresponding new Materials and Domains.

Note that we have to redefine the GeoBase object rotor here, as we can't be adding stuff to an already-meshed geometry.

rotor = RadialGeometry(dimensions);

Air = Material.create(0);
Core = Domain('Core', Iron, s_core);
Polegap = Domain('Polegap', Air, s_air_cw, s_air_ccw);

rotor.add_material(Iron, Air);
rotor.add_domain(Core, Polegap);

Again, we create the elementary mesh...

rotor.mesh_elementary_geometry();
clf; hold on; box on; axis equal;
rotor.triplot('Core', 'k');
rotor.triplot('Polegap', 'r');

and then replicate it to cover the symmetry sector.

rotor.replicate_elementary_mesh(  )

A Closer look at GeoBase

Earlier, we saw that after meshing the elementary geometry, it was easy to e.g. triplot the Core Domain by referring to its name 'Core'.

Under the hood, this calls, the get_domain method of GeoBase. However, after replicating the elementary mesh, the rotor domain names are a little different:

rotor.domains(1:4).name
ans =

    'Core_1'


ans =

    'Polegap_1'


ans =

    'Core_2'


ans =

    'Polegap_2'

Indeed, each elementary segment of the replicated geometry now has its own Domain. Furthermore, the underscored number at the end of each domain name indicates which replicated sector it belongs to.

clf; hold on; box on; axis equal tight;
rotor.fill('Core_1', [1 1 1]*0.3);
rotor.fill('Core_2', [1 1 1]*0.5);
snapnow

However, thanks to how get_domain is implemented, we can also use the asterisk (*) wildcard character to get all domains corresponding to the original one:

clf; hold on; box on; axis equal tight;
rotor.fill('Core_*', [1 1 1]*0.65);
rotor.fill('Polegap_*', 'y');
snapnow;

Finally, remember those special Curves we named while defining the Surfaces wayyy back above? Namely:

Turns out, we can find the nodes of them all as properties of the Geometry object:

rotor.plot(rotor.n_cw, 'ro-', 'Linewidth', 2);
rotor.plot(rotor.n_ccw, 'gd-', 'Linewidth', 2);
rotor.plot(rotor.n_ag, 'bx-');

What to read next?

Next, you'll learn how to use your newly-created geometries in actual analysis. You'll learn, for instance, how to calculate:

All this, and much more, you'll learn in the Analysis Tutorial 1.

Well, you will as soon as the tutorial gets published. To receive an email update when it does, please fill out this really short single-page survey here.

(Back to index)