Submodule: mfs.helpers

Assistance functions

Overview

Rotation matricies

mfs.helpers.rotate_around_z(theta)

Calculcate the rotation matrix to rotate around the Z-axis by angle theta (radians)

mfs.helpers.rotate_around_x(phi)

Calculcate the rotation matrix to rotate around the X-axis by angle phi (radians)

mfs.helpers.rotate_to_dashed_frame(r, theta, phi)

Rotate the 3-element vector r around the X-axis by phi, and then around the Z-axis by theta

mfs.helpers.rotate_to_normal_frame(rDash, …)

Rotate the 3-element array rDash, that exists in the dashed frame defined by theta and phi, back into the global frame.

Display magnetic field

mfs.helpers.plot_scalar_B_field(magnets, …)

Plot the magnitude of the B-field along a 1D axis

mfs.helpers.plot_vector_B_field(magnets, …)

Produce a 2D quiver plot of the vector B-field arising from a set of magnets

mfs.helpers.print_field_gradient(magnets, centre)

Print the value of the B-field gradient along the x and y axes around centre in G/cm

Detailed Documentation

mfs.helpers.evaluate_axis_projection(projection)

Interpret a 2D projection request

Interprets a string containing the unique letters ‘xyz’ into indicies for reading values out of an array and plotting a projection. The first letter will go on the graph’s X-axis, the 2nd letter on the graph’s Y axis.

For example, ‘zyx’ or ‘zy’ will result in projecting the simulation’s Z-axis onto the horizontal axis of a 2D graph (and label it accordingly), and the simulation’s Y-axis onto the vertical axis of the 2D graph.

Parameters
projection: str

Desired projection order

Returns
axOnePos: int

The index of the simulation axis that will be placed on the graph’s first (x) axis. For example, if the string was ‘zyx’, this will have the value 2

axTwoPos: int

The index of the simulation axis that will be placed on the graph’s second (y) axis. For example, if the string was ‘zyx’, this will have the value 1

axThreePos: int

The index of the simulation axis that will be placed on the graph’s third (z) axis. For example, if the string was ‘zyx’, this will have the value 0

mfs.helpers.plot_scalar_B_field(magnets, axes, centre, limit, projection, points)

Plot the magnitude of the B-field along a 1D axis

Axis must be parallel to one of the global cardinal dimensions.

Parameters
magnets: magnet or list of magnets

Group of magnets over which to calculate

axes: matplotlib.Axes

axes on which to display the plot. Produced by, e.g., fig, axes = plt.subplots()

centre: 3-element array

(x, y, z) in global frame

limit: float

+- limit of the axis along which to calculate field. The axis goes from (centre-lim) to (centre+lim)

projection: str

Which cardinal axis is your axis parallel to

points: Number of points at which to calculate B-field.
Returns
np.ndarray

Axis of locations along projection at which values were calculated

np.ndarray

Values of magnitude of B-field along axis

mfs.helpers.plot_vector_B_field(magnets, axes, centre, limit, projection, points=50, threads=1)

Produce a 2D quiver plot of the vector B-field arising from a set of magnets

Plot a grid of arrows indicating the direction and magnitude of the vector B-field. The values are calculated in a flat plane in the global frame, determined by centre, projection, and limit.

The grid is points * points in size, covering the area centre-limit to centre+limit

For large numbers of magnets, multiprocessing is recommended to speed up the calculations. Additional processes can be spawned with the threads argument. Particularly recommended with spatially distributed CoilPairs.

Parameters
magnets: mfs.Magnet or list of mfs.Magnet

All magnet sources to include in the calculation. Should be descendents of mfs.Magnet

axes: matplotlib.Axes

Matplotlib axes objects on which to display the resulting figure

centre: np.ndarray

Origin of the grid over which the B field is calculated: r=(x, y, z)

limit: float

half-width of the grid over which B-field is calculated The total grid is calculated as (r-limit, r+limit)

projection: str

Projection over which to evaluate the B-field, see mfs.helpers.evaluate_axis_projection

points: int

Number of points along each edge of the grid. Computational complexity scales as O(points^2)

threads: int

Number of concurrent processes. Defaults to the number of logical CPUs available. Operates on a single process if given either 0 or 1 For a small number of magnets, single-threading is dramatically faster due to the overhead of spawning new processes.

Returns
np.ndarray

points x points array storing the i'th component of the B field at those locations. i determed as the first element of projection

np.ndarray

points x points array storing the j'th component of the B field at those locations. j determed as the second element of projection

mfs.helpers.print_field_gradient(magnets, centre, label='')

Print the value of the B-field gradient along the x and y axes around centre in G/cm

mfs.helpers.rotate_around_x(phi)

Calculcate the rotation matrix to rotate around the X-axis by angle phi (radians)

Parameters
phi: float

Rotation angle in radians

Returns
np.ndarray

3x3 array. May be multiplied with a 3-element vector to rotate that vector around the X-axis by theta

mfs.helpers.rotate_around_z(theta)

Calculcate the rotation matrix to rotate around the Z-axis by angle theta (radians)

Parameters
theta: float

Rotation angle in radians

Returns
np.ndarray

3x3 array. May be multiplied with a 3-element vector to rotate that vector around the Z-axis by theta

mfs.helpers.rotate_to_dashed_frame(r, theta, phi)

Rotate the 3-element vector r around the X-axis by phi, and then around the Z-axis by theta

theta and phi are extrinsic angles, i.e. the angles remain relative to the global frame, regardless of how many or what rotations are performed.

The order is relevant, consider the following example, in which we rotate the y-unit-vector (0, 1, 0) first by phi=45°, and second by theta=90°

The first rotation, around X, should give us a diagonal in the YZ plane, i.e. 1/sqrt(2) (0, 1, 1)

The second rotation takes that diagonal, and rotates it around Z into the XZ plane, to 1/sqrt(2) (-1, 0, 1)

We can process this in two separate, discrete, steps

>>> unit_y = np.array([0,1,0])
>>> stage_1a = rotate_to_dashed_frame(unit_y, theta=0, phi=np.radians(45))
>>> stage_1a
array(0, 0.7071, 0.7071 )
>>> stage_2a = rotate_to_dashed_frame(stage_1a, theta=np.radians(90), phi=0)
>>> stage_2a
array(-0.7071, 0, 0.7071)

We can consider what the outcome would be if the rotation occurred in the reverse order: i.e. the y-unit-vector rotated first by theta=90° and then by phi=45°

The first rotation, around Z, should give us (-1, 0, 0)

The second rotation, around X, should then have no effect at all, because the vector is on the x-axis.

>>> unit_y = np.array([0,1,0])
>>> stage_1b = rotate_to_dashed_frame(unit_y, theta=np.radians(90), phi=0)
>>> stage_1b
array(-1, 0, 0)
>>> stage_2b = rotate_to_dashed_frame(stage_1b, theta=0, phi=np.radians(45))
>>> stage_2b
array(-1, 0, 0)

We can verify that thisfunction behaves in the order shown by the first case in two ways

  1. by examining the matrix multiplication implemented

  2. By comparing the outcome to the two examples given

>>> rotate_to_dashed_frame(unit_y, theta=np.radians(90), phi=np.radians(45))
array (-0.7071, 0, 0.7071)

As we can see, the outcome matches the example given first, i.e. X then Y.

Parameters
r: array-like

Vector to rotate, must be 3 elements.

theta: float

Angle (in radians) to rotate around the Z-axis

phi: float

Angle (in radians) to rotate around the X-axis

Returns
np.ndarray

Rotated array

mfs.helpers.rotate_to_normal_frame(rDash, theta, phi)

Rotate the 3-element array rDash, that exists in the dashed frame defined by theta and phi, back into the global frame.

The inverse transformation to rotate_to_dashed_frame. The arguments theta and phi should be given as the same values used to transform to the dashed frame to begin with - i.e. these are the angles that define the dashed frame, and not the transformation from the dashed frame, per se.

>>> unit_y = np.array([0,1,0])
>>> theta = np.radians(30)
>>> phi = np.radians(45)
>>> unit_y_dash = rotate_to_dashed_frame(unit_y, theta, phi)
>>> unit_y_dash
np.array( -0.3536, 0.6123, 0.7071)

If we attempt to transform back with the same angles, we get the original vector

>>> rotate_to_normal_frame(unit_y_dash, theta, phi)
np.array(0, 1, 0)

If we attempt to rotate back with the opposite angles, we get something else entirely

>>> rotate_to_dashed_frame(unit_y_dash, -theta, -phi)
np.array(-0.6124, -0.25, 0.75)
Parameters
rDash: array-like

Vector in Dashed frame to transform back to global frame

theta: float

angle (in radians) that defines the dashed frame rotation around the Z axis

phi: float

angle (in radians) that defines the dashed frame rotation around the X axis