Skip to content

Utils Module

pymagnets.utils

This module imports the classes and functions in the private modules to create a public API, including:

  • Quaternion()
  • Global Constants
  • Point structures
  • Vector structures

global_const

Global Constants PI, PI/2, PI/4, µ0, and three internally used constants: FP_CUTOFF = 1e-8, ALIGN_CUTOFF = 1e-5, MAG_TOL = 1e-4

Routines for converting between coordinate systems, between 2D cartesian and polar, as well as 3D cartesian, cylindrical, and spherical.

cart2pol(x, y)

Converts from cartesian to polar coordinates

Parameters:

Name Type Description Default
x ndarray

x coordinates

required
y ndarray

y coordinates

required

Returns:

Type Description
tuple

rho, phi

Source code in pymagnet/utils/_conversions.py
def cart2pol(x, y):
    """Converts from cartesian to polar coordinates

    Args:
        x (ndarray): x coordinates
        y (ndarray): y coordinates

    Returns:
        tuple: rho, phi
    """
    rho = _np.sqrt(x ** 2 + y ** 2)
    phi = _np.arctan2(y, x)
    return (rho, phi)

cart2sph(x, y, z)

Converts from cartesian to spherical coordinates

Parameters:

Name Type Description Default
x ndarray

x coordinates

required
y ndarray

y coordinates

required
z ndarray

z coordinates

required

Returns:

Type Description
tuple

r, theta, phi

Source code in pymagnet/utils/_conversions.py
def cart2sph(x, y, z):
    """Converts from cartesian to spherical coordinates

    Args:
        x (ndarray): x coordinates
        y (ndarray): y coordinates
        z (ndarray): z coordinates

    Returns:
        tuple: r, theta, phi
    """
    r = _np.sqrt(x ** 2 + y ** 2 + z ** 2)
    phi = _np.arctan2(y, x)

    # Hide the warning for situtations where there is a divide by zero.
    # This returns a NaN in the array, which is ignored for plotting.
    with _np.errstate(divide="ignore", invalid="ignore"):
        theta = _np.arccos(z / r)
    return (r, theta, phi)

get_unit_value_meter(unit)

Returns a queried metre unit as a number

Examples:

factor = get_unit_value_meter('cm') print(f"factor for 'cm' is {factor}")

Parameters:

Name Type Description Default
unit string

SI length unit

required

Returns:

Type Description
float

SI prefix factor

Source code in pymagnet/utils/_conversions.py
def get_unit_value_meter(unit):
    """Returns a queried metre unit as a number
    Example:
        factor = get_unit_value_meter('cm')
        print(f"factor for 'cm' is {factor}")

    Args:
        unit (string): SI length unit

    Returns:
        float: SI prefix factor
    """
    si_prefixes = {
        "Ym": 1e24,
        "Zm": 1e21,
        "Em": 1e18,
        "Pm": 1e15,
        "Tm": 1e12,
        "Gm": 1e9,
        "Mm": 1e6,
        "km": 1e3,
        "hm": 1e2,
        "dam": 1e1,
        "m": 1,
        "dm": 1e-1,
        "cm": 1e-2,
        "mm": 1e-3,
        "µm": 1e-6,
        "um": 1e-6,
        "nm": 1e-9,
        "Ang": 1e-10,
        "pm": 1e-12,
        "fm": 1e-15,
        "am": 1e-18,
        "zm": 1e-21,
        "ym": 1e-24,
    }

    return si_prefixes.get(unit, None)

get_unit_value_tesla(unit)

Returns a queried magnetic flux density unit as a number

Examples:

factor = get_unit_value_meter('mT') print(f"factor for 'mT' is {factor}")

Parameters:

Name Type Description Default
unit string

SI length unit

required

Returns:

Type Description
float

SI prefix factor

Source code in pymagnet/utils/_conversions.py
def get_unit_value_tesla(unit):
    """Returns a queried magnetic flux density unit as a number
    Example:
        factor = get_unit_value_meter('mT')
        print(f"factor for 'mT' is {factor}")

    Args:
        unit (string): SI length unit

    Returns:
        float: SI prefix factor
    """

    si_prefixes = {
        "YT": 1e24,
        "ZT": 1e21,
        "ET": 1e18,
        "PT": 1e15,
        "TT": 1e12,
        "GT": 1e9,
        "MT": 1e6,
        "kT": 1e3,
        "hT": 1e2,
        "daT": 1e1,
        "T": 1,
        "dT": 1e-1,
        "cT": 1e-2,
        "mT": 1e-3,
        "µT": 1e-6,
        "uT": 1e-6,
        "nT": 1e-9,
        "pT": 1e-12,
        "fT": 1e-15,
        "aT": 1e-18,
        "zT": 1e-21,
        "yT": 1e-24,
    }

    return si_prefixes.get(unit, None)

pol2cart(rho, phi)

Converts from polar to cartesian coordinates

Parameters:

Name Type Description Default
rho ndarray

radial coordinates

required
phi ndarray

azimuthal coordinates

required

Returns:

Type Description
tuple

x,y

Source code in pymagnet/utils/_conversions.py
def pol2cart(rho, phi):
    """Converts from polar to cartesian coordinates

    Args:
        rho (ndarray): radial coordinates
        phi (ndarray): azimuthal coordinates

    Returns:
        tuple: x,y
    """
    x = rho * _np.cos(phi)
    y = rho * _np.sin(phi)
    return (x, y)

sph2cart(r, theta, phi)

Converts from spherical to cartesian coordinates

Parameters:

Name Type Description Default
r ndarray

radial coordinates

required
theta ndarray

azimuthal angles

required
phi ndarray

polar angle

required

Returns:

Type Description
tuple

x,y,z

Source code in pymagnet/utils/_conversions.py
def sph2cart(r, theta, phi):
    """Converts from spherical to cartesian coordinates

    Args:
        r (ndarray): radial coordinates
        theta (ndarray): azimuthal angles
        phi (ndarray): polar angle

    Returns:
        tuple: x,y,z
    """
    x = r * _np.sin(theta) * _np.cos(phi)
    y = r * _np.sin(theta) * _np.sin(phi)
    z = r * _np.cos(theta)
    return x, y, z

sphere_sph2cart(Br, Btheta, theta, phi)

Converts magnetic field of a sphere from spherical to cartesian coordinates

Parameters:

Name Type Description Default
Br ndarray

radial vector component

required
Btheta ndarray

polar vector component

required
theta ndarray

azimuthal angles

required
phi ndarray

polar angle

required

Returns:

Type Description
tuple

Bx,By,Bz

Source code in pymagnet/utils/_conversions.py
def sphere_sph2cart(Br, Btheta, theta, phi):
    """Converts magnetic field of a sphere from spherical to cartesian coordinates

    Args:
        Br (ndarray): radial vector component
        Btheta (ndarray): polar vector component
        theta (ndarray): azimuthal angles
        phi (ndarray): polar angle

    Returns:
        tuple: Bx,By,Bz
    """
    Bx = Br * _np.sin(theta) * _np.cos(phi) + Btheta * _np.cos(theta) * _np.cos(phi)

    By = Br * _np.sin(theta) * _np.sin(phi) + Btheta * _np.cos(theta) * _np.sin(phi)

    Bz = Br * _np.cos(theta) - Btheta * _np.sin(theta)
    return Bx, By, Bz

vector_pol2cart(Brho, Bphi, phi)

Converts Vectors from polar to cartesian coordinates

Parameters:

Name Type Description Default
Brho ndarray

radial vector component

required
Bphi ndarray

azimuthal vector component

required
phi ndarray

azimuthal coordinates

required

Returns:

Type Description
tuple

Bx, By

Source code in pymagnet/utils/_conversions.py
def vector_pol2cart(Brho, Bphi, phi):
    """Converts Vectors from polar to cartesian coordinates

    Args:
        Brho (ndarray): radial vector component
        Bphi (ndarray): azimuthal vector component
        phi (ndarray): azimuthal coordinates

    Returns:
        tuple: Bx, By
    """
    Bx = Brho * _np.cos(phi) - Bphi * _np.sin(phi)
    By = Brho * _np.sin(phi) + Bphi * _np.cos(phi)
    return Bx, By

vector_sph2cart(Br, Btheta, Bphi, theta, phi)

Converts Vectors from spherical to cartesian coordinates

Parameters:

Name Type Description Default
Br ndarray

radial vector component

required
Btheta ndarray

polar vector component

required
Bphi ndarray

azimuthal vector component

required
theta ndarray

azimuthal angles

required
phi ndarray

polar angle

required

Returns:

Type Description
tuple

Bx,By,Bz

Source code in pymagnet/utils/_conversions.py
def vector_sph2cart(Br, Btheta, Bphi, theta, phi):
    """Converts Vectors from spherical to cartesian coordinates

    Args:
        Br (ndarray): radial vector component
        Btheta (ndarray): polar vector component
        Bphi (ndarray): azimuthal vector component
        theta (ndarray): azimuthal angles
        phi (ndarray): polar angle

    Returns:
        tuple: Bx,By,Bz
    """
    Bx = (
        Br * _np.sin(theta) * _np.cos(phi)
        + Btheta * _np.cos(theta) * _np.cos(phi)
        - Bphi * _np.sin(phi)
    )

    By = (
        Br * _np.sin(theta) * _np.sin(phi)
        + Btheta * _np.cos(theta) * _np.sin(phi)
        + Bphi * _np.cos(phi)
    )

    Bz = Br * _np.cos(theta) - Btheta * _np.sin(theta)
    return Bx, By, Bz

pymagnets.utils._point_structs

Private module consiting of point classes and their methods.

Point2

2D point class

Note that multiplication of two points is done elementwise, dot product is a separate method.

Source code in pymagnet/utils/_point_structs.py
class Point2(object):
    """2D point class

    Note that multiplication of two points is done elementwise, dot product is
    a separate method.
    """

    def __init__(self, x, y):
        """Init method

        Args:
            x (ndarray): x coordinates
            y (ndarray): y coordinates
        """
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"({self.x}, {self.y})"

    def __str__(self) -> str:
        return f"({self.x}, {self.y})"

    def __add__(self, other):
        return Point2(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Point2(self.x - other.x, self.y - other.y)

    def __mul__(self, other):
        return Point2(self.x * other.x, self.y * other.y)

    def __div__(self, other):
        x = (self.x / other.x) if other.x != 0 else 0
        y = (self.y / other.y) if other.y != 0 else 0
        return Point2(x, y)

    def __lt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag < other_mag

    def __le__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag <= other_mag

    def __gt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag > other_mag

    def __ge__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag >= other_mag

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __ne__(self, other):
        return self.x != other.x or self.y != other.y

    def distance_to(self, point):
        """Calculates distance to a point

        Args:
            point (Point2): Target point

        Returns:
            float: distance
        """
        return _np.sqrt(_np.power(point.x - self.x, 2) + _np.power(point.y - self.y, 2))

    def distance_to_origin(self):
        """Calculates distance to a origin

        Returns:
            float: distance
        """
        return self._norm()

    def _norm(self):
        """Norm of Point

        Returns:
            float: norm
        """
        return _np.linalg.norm([self.x, self.y], axis=0)

__init__(self, x, y) special

Init method

Parameters:

Name Type Description Default
x ndarray

x coordinates

required
y ndarray

y coordinates

required
Source code in pymagnet/utils/_point_structs.py
def __init__(self, x, y):
    """Init method

    Args:
        x (ndarray): x coordinates
        y (ndarray): y coordinates
    """
    self.x = x
    self.y = y

distance_to(self, point)

Calculates distance to a point

Parameters:

Name Type Description Default
point Point2

Target point

required

Returns:

Type Description
float

distance

Source code in pymagnet/utils/_point_structs.py
def distance_to(self, point):
    """Calculates distance to a point

    Args:
        point (Point2): Target point

    Returns:
        float: distance
    """
    return _np.sqrt(_np.power(point.x - self.x, 2) + _np.power(point.y - self.y, 2))

distance_to_origin(self)

Calculates distance to a origin

Returns:

Type Description
float

distance

Source code in pymagnet/utils/_point_structs.py
def distance_to_origin(self):
    """Calculates distance to a origin

    Returns:
        float: distance
    """
    return self._norm()

Point3 (Point2)

3D point class

Note that multiplication of two points is done elementwise, dot product is a separate method.

Source code in pymagnet/utils/_point_structs.py
class Point3(Point2):
    """3D point class

    Note that multiplication of two points is done elementwise, dot product is
    a separate method.

    """

    def __init__(self, x, y, z):
        super().__init__(x, y)
        self.z = z

    def __repr__(self) -> str:
        return f"({self.x}, {self.y}, {self.z})"

    def __str__(self) -> str:
        return f"({self.x}, {self.y}, {self.z})"

    def __add__(self, other):
        return Point3(self.x + other.x, self.y + other.y, self.z + other.z)

    def __sub__(self, other):
        return Point3(self.x - other.x, self.y - other.y, self.z - other.z)

    def __mul__(self, other):
        return Point3(self.x * other.x, self.y * other.y, self.z * other.z)

    def __div__(self, other):
        x = (self.x / other.x) if other.x != 0 else 0
        y = (self.y / other.y) if other.y != 0 else 0
        z = (self.z / other.z) if other.z != 0 else 0
        return Point3(x, y, z)

    def __lt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag < other_mag

    def __le__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag <= other_mag

    def __gt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag > other_mag

    def __ge__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag >= other_mag

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __ne__(self, other):
        return self.x != other.x or self.y != other.y

    def distance_to(self, point):
        return _np.sqrt(
            _np.power(point.x - self.x, 2)
            + _np.power(point.y - self.y, 2)
            + _np.power(point.z - self.z, 2)
        )

    def _norm(self):
        return _np.linalg.norm([self.x, self.y, self.z], axis=0)

    def distance_to_origin(self):
        return self._norm()

distance_to(self, point)

Calculates distance to a point

Parameters:

Name Type Description Default
point Point2

Target point

required

Returns:

Type Description
float

distance

Source code in pymagnet/utils/_point_structs.py
def distance_to(self, point):
    return _np.sqrt(
        _np.power(point.x - self.x, 2)
        + _np.power(point.y - self.y, 2)
        + _np.power(point.z - self.z, 2)
    )

distance_to_origin(self)

Calculates distance to a origin

Returns:

Type Description
float

distance

Source code in pymagnet/utils/_point_structs.py
def distance_to_origin(self):
    return self._norm()

Quaternion module

Implements quaternion multiplication for convenient rotation of vectors in 3D.

Examples:

Rotation of a vector about the x-axis:

import numpy as np
import pymagnet as pm
vector1 = np.array([1,0,0])
rotate_about_z = pm.magnets.Quaternion.q_angle_from_axis(np.pi/2, (0, 0, 1))
vector2 = rotate_about_z * vector1

Quaternion

Quaternion class. overloading of multiplication symbol allows easy quaternion multiplications

Source code in pymagnet/utils/_quaternion.py
class Quaternion:
    """Quaternion class.
    overloading of multiplication symbol allows easy quaternion multiplications

    """

    def __init__(self, w=1.0, x=0.0, y=0.0, z=0.0):
        """Initialse a pure quaternion (1; 0, 0, 0)

        Args:
            w (ndarray, optional): scalar quaternion. Defaults to 1.0.
            x (ndarray, optional): vector component. Defaults to 0.0.
            y (ndarray, optional): vector component. Defaults to 0.0.
            z (ndarray, optional): vector component. Defaults to 0.0.
        """
        self.w = _np.asarray(w)
        self.x = _np.asarray(x)
        self.y = _np.asarray(y)
        self.z = _np.asarray(z)

    def q_angle_from_axis(theta, vec):
        """Generates a rotation quaternion for an angle `theta` about an axis `vec`
        This is a normailsed, i.e. unit quaternion.

        Args:
            theta (float): angle of rotation
            vec (tuple/array): axis vector

        Example:
            90 degree rotation about the x axis:

                rotation_quaternion = Quaternion.q_angle_from_axis(np.pi/2, (1, 0, 0) )

        Returns:
            Quaternion: rotation quaternion
        """
        vec = Quaternion._normalise_axis(vec)
        w = _np.cos(theta / 2.0)
        vec *= _np.sin(theta / 2.0)
        x = vec[0]
        y = vec[1]
        z = vec[2]
        rotation_quaternion = Quaternion(w, x, y, z)
        return rotation_quaternion

    def get_conjugate(self):
        """Returns quaternion conjugate

        Returns:
            quaternion: quaternion conjugate
        """
        return Quaternion(self.w, -self.x, -self.y, -self.z)

    @staticmethod
    def gen_rotation_quaternion(alpha_rad=0.0, beta_rad=0.0, gamma_rad=0.0):
        """Generates quaternion for rotation around z, y, x axes

        Args:
            alpha_rad (float): angle to z-axis. Defaults to 0.0.
            beta_rad (float): angle to y-axis. Defaults to 0.0.
            gamma_rad (float): angle to x-axis. Defaults to 0.0.

        Returns:
            Quaternion: rotation quaternion
        """

        rotate_about_x = Quaternion()
        rotate_about_y = Quaternion()
        rotate_about_z = Quaternion()

        forward_rotation = Quaternion()

        if _np.fabs(alpha_rad) > MAG_TOL:
            rotate_about_z = Quaternion.q_angle_from_axis(alpha_rad, (0, 0, 1))

        if _np.fabs(beta_rad) > MAG_TOL:
            rotate_about_y = Quaternion.q_angle_from_axis(beta_rad, (0, 1, 0))

        if _np.fabs(gamma_rad) > MAG_TOL:
            rotate_about_x = Quaternion.q_angle_from_axis(gamma_rad, (1, 0, 0))

        forward_rotation = rotate_about_x * rotate_about_z * rotate_about_y

        return forward_rotation

    @staticmethod
    def _prepare_vector(x, y, z):
        """Creates 3xN array where each column corresponds to x, y, z vectors
        all of the same length. If some vectors are shorter, their values are repeated
        to ensure and array of (x,y,z) points for quaternion rotation.

        Args:
            x (ndarray): x coordinates
            y (ndarray): y coordinates
            z (ndarray): z coordinates

        Returns:
            ndarray: 3xN numpy ndarray
        """
        # Check input x,y,z are numpy arrays and if not, convert to numpy arrays
        x = _np.asarray(x)
        y = _np.asarray(y)
        z = _np.asarray(z)

        longest_array_length = _np.max([x.size, y.size, z.size])

        # Ensure the unravelled arrays are all of the same length
        x = Quaternion._check_extend_array(x.ravel(), longest_array_length)
        y = Quaternion._check_extend_array(y.ravel(), longest_array_length)
        z = Quaternion._check_extend_array(z.ravel(), longest_array_length)

        return _np.array([x, y, z])

    @staticmethod
    def _check_extend_array(array, max):
        """Extend a 1D vector to length 'max' if it is shorter than 'max'

        Args:
            array (ndarray): numpy array
            max (int): array length to compare to

        Returns:
            ndarray: numpy array of length 'max'
        """

        if max % array.size != 0:
            raise ValueError("Incorrect gridding of x,y,z")
        # If array is smaller than longest array length 'max', extend it by tiling
        if array.size < max:
            extended_array = _np.tile(array, (max // array.size))

            # # If there is a remainder, 'n' append the first n elements of the array
            # # to ensure the final length is 'max'.
            # if max % array.size != 0:
            #     extended_array = _np.append(
            #         extended_array, array[0 : (max % array.size)]
            #     )
            return extended_array
        else:
            return array

    @staticmethod
    def _normalise_axis(vec):
        """Normalise

        Args:
            v (array): [description]

        Returns:
            [type]: [description]
        """
        vec = _np.asarray(vec)
        if _np.fabs(_np.linalg.norm(vec, axis=0)) < FP_CUTOFF:
            raise ValueError("Vec norm should be non-zero")
        return vec / _np.linalg.norm(vec, axis=0)

    @staticmethod
    def vec_norm(x, y, z):
        """Normalises each x,y,z vector

        Args:
            x (ndarray): x array
            y (ndarray): y array
            z (ndarray): z array

        Returns:
            array: 3xN array of normalised vectors
        """
        vec = Quaternion._prepare_vector(x, y, z)
        return _np.linalg.norm(vec, axis=0)

    def __repr__(self):
        str = f"({self.w}; {self.x}, {self.y}, {self.z})"
        return str

    def __mul__(self, b):
        if isinstance(b, Quaternion):
            return self._multiply_with_quaternion(b)
        elif isinstance(b, (list, tuple, _np.ndarray)):
            if len(b) != 3:
                raise Exception(f"Input vector has invalid length {len(b)}")
            return self._multiply_with_vector(b)
        else:
            raise Exception(f"Multiplication with unknown type {type(b)}")

    def as_tuple(self):
        """Returns quaternion as tuple of arrays

        Returns:
            tuple: w (array), x (array), y (array), z (array)
        """
        return self.w, self.x, self.y, self.z

    def _multiply_with_quaternion(self, q2):
        """Computes the Hamilton product of two quaternions

        Args:
            q2 (quaternion): quaternion

        Returns:
            quaternion: Hamilton product
        """
        w1, x1, y1, z1 = self.as_tuple()
        w2, x2, y2, z2 = q2.as_tuple()
        w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
        x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
        y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
        z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
        result = Quaternion(w, x, y, z)
        return result

    def _multiply_with_vector(self, v):
        """Converts vector ``v`` to quaternion ``q2`` and then performs rotation by
        multiplying `q * q2 * q'`


        Args:
            v (vector/array): vector to be rotated

        Returns:
            tuple: multiplied vector x', y', z'
        """
        q2 = Quaternion(_np.zeros_like(v[0]), v[0], v[1], v[2])
        result = self * q2 * self.get_conjugate()
        return result.x, result.y, result.z

    def get_axisangle(self):
        theta = _np.arccos(self.w) * 2.0
        vec = _np.hstack([self.x, self.y, self.z])
        return theta, self._normalise_axis(vec)

    @staticmethod
    def euler_to_quaternion(alpha, beta, gamma):
        """Converts Euler angles to quaternion

        Args:
            alpha (float): angle to z-axis
            beta (float): angle to y-axis
            gamma (float): angle to x-axis

        Returns:
            Quaternion: Euler angles as a Quaternion
        """

        qw = _np.cos(alpha / 2) * _np.cos(beta / 2) * _np.cos(gamma / 2) + _np.sin(
            alpha / 2
        ) * _np.sin(beta / 2) * _np.sin(gamma / 2)
        qx = _np.sin(alpha / 2) * _np.cos(beta / 2) * _np.cos(gamma / 2) - _np.cos(
            alpha / 2
        ) * _np.sin(beta / 2) * _np.sin(gamma / 2)
        qy = _np.cos(alpha / 2) * _np.sin(beta / 2) * _np.cos(gamma / 2) + _np.sin(
            alpha / 2
        ) * _np.cos(beta / 2) * _np.sin(gamma / 2)
        qz = _np.cos(alpha / 2) * _np.cos(beta / 2) * _np.sin(gamma / 2) - _np.sin(
            alpha / 2
        ) * _np.sin(beta / 2) * _np.cos(gamma / 2)

        return Quaternion(qw, qx, qy, qz)

__init__(self, w=1.0, x=0.0, y=0.0, z=0.0) special

Initialse a pure quaternion (1; 0, 0, 0)

Parameters:

Name Type Description Default
w ndarray

scalar quaternion. Defaults to 1.0.

1.0
x ndarray

vector component. Defaults to 0.0.

0.0
y ndarray

vector component. Defaults to 0.0.

0.0
z ndarray

vector component. Defaults to 0.0.

0.0
Source code in pymagnet/utils/_quaternion.py
def __init__(self, w=1.0, x=0.0, y=0.0, z=0.0):
    """Initialse a pure quaternion (1; 0, 0, 0)

    Args:
        w (ndarray, optional): scalar quaternion. Defaults to 1.0.
        x (ndarray, optional): vector component. Defaults to 0.0.
        y (ndarray, optional): vector component. Defaults to 0.0.
        z (ndarray, optional): vector component. Defaults to 0.0.
    """
    self.w = _np.asarray(w)
    self.x = _np.asarray(x)
    self.y = _np.asarray(y)
    self.z = _np.asarray(z)

as_tuple(self)

Returns quaternion as tuple of arrays

Returns:

Type Description
tuple

w (array), x (array), y (array), z (array)

Source code in pymagnet/utils/_quaternion.py
def as_tuple(self):
    """Returns quaternion as tuple of arrays

    Returns:
        tuple: w (array), x (array), y (array), z (array)
    """
    return self.w, self.x, self.y, self.z

euler_to_quaternion(alpha, beta, gamma) staticmethod

Converts Euler angles to quaternion

Parameters:

Name Type Description Default
alpha float

angle to z-axis

required
beta float

angle to y-axis

required
gamma float

angle to x-axis

required

Returns:

Type Description
Quaternion

Euler angles as a Quaternion

Source code in pymagnet/utils/_quaternion.py
@staticmethod
def euler_to_quaternion(alpha, beta, gamma):
    """Converts Euler angles to quaternion

    Args:
        alpha (float): angle to z-axis
        beta (float): angle to y-axis
        gamma (float): angle to x-axis

    Returns:
        Quaternion: Euler angles as a Quaternion
    """

    qw = _np.cos(alpha / 2) * _np.cos(beta / 2) * _np.cos(gamma / 2) + _np.sin(
        alpha / 2
    ) * _np.sin(beta / 2) * _np.sin(gamma / 2)
    qx = _np.sin(alpha / 2) * _np.cos(beta / 2) * _np.cos(gamma / 2) - _np.cos(
        alpha / 2
    ) * _np.sin(beta / 2) * _np.sin(gamma / 2)
    qy = _np.cos(alpha / 2) * _np.sin(beta / 2) * _np.cos(gamma / 2) + _np.sin(
        alpha / 2
    ) * _np.cos(beta / 2) * _np.sin(gamma / 2)
    qz = _np.cos(alpha / 2) * _np.cos(beta / 2) * _np.sin(gamma / 2) - _np.sin(
        alpha / 2
    ) * _np.sin(beta / 2) * _np.cos(gamma / 2)

    return Quaternion(qw, qx, qy, qz)

gen_rotation_quaternion(alpha_rad=0.0, beta_rad=0.0, gamma_rad=0.0) staticmethod

Generates quaternion for rotation around z, y, x axes

Parameters:

Name Type Description Default
alpha_rad float

angle to z-axis. Defaults to 0.0.

0.0
beta_rad float

angle to y-axis. Defaults to 0.0.

0.0
gamma_rad float

angle to x-axis. Defaults to 0.0.

0.0

Returns:

Type Description
Quaternion

rotation quaternion

Source code in pymagnet/utils/_quaternion.py
@staticmethod
def gen_rotation_quaternion(alpha_rad=0.0, beta_rad=0.0, gamma_rad=0.0):
    """Generates quaternion for rotation around z, y, x axes

    Args:
        alpha_rad (float): angle to z-axis. Defaults to 0.0.
        beta_rad (float): angle to y-axis. Defaults to 0.0.
        gamma_rad (float): angle to x-axis. Defaults to 0.0.

    Returns:
        Quaternion: rotation quaternion
    """

    rotate_about_x = Quaternion()
    rotate_about_y = Quaternion()
    rotate_about_z = Quaternion()

    forward_rotation = Quaternion()

    if _np.fabs(alpha_rad) > MAG_TOL:
        rotate_about_z = Quaternion.q_angle_from_axis(alpha_rad, (0, 0, 1))

    if _np.fabs(beta_rad) > MAG_TOL:
        rotate_about_y = Quaternion.q_angle_from_axis(beta_rad, (0, 1, 0))

    if _np.fabs(gamma_rad) > MAG_TOL:
        rotate_about_x = Quaternion.q_angle_from_axis(gamma_rad, (1, 0, 0))

    forward_rotation = rotate_about_x * rotate_about_z * rotate_about_y

    return forward_rotation

get_conjugate(self)

Returns quaternion conjugate

Returns:

Type Description
quaternion

quaternion conjugate

Source code in pymagnet/utils/_quaternion.py
def get_conjugate(self):
    """Returns quaternion conjugate

    Returns:
        quaternion: quaternion conjugate
    """
    return Quaternion(self.w, -self.x, -self.y, -self.z)

q_angle_from_axis(theta, vec)

Generates a rotation quaternion for an angle theta about an axis vec This is a normailsed, i.e. unit quaternion.

Parameters:

Name Type Description Default
theta float

angle of rotation

required
vec tuple/array

axis vector

required

Examples:

90 degree rotation about the x axis:

rotation_quaternion = Quaternion.q_angle_from_axis(np.pi/2, (1, 0, 0) )

Returns:

Type Description
Quaternion

rotation quaternion

Source code in pymagnet/utils/_quaternion.py
def q_angle_from_axis(theta, vec):
    """Generates a rotation quaternion for an angle `theta` about an axis `vec`
    This is a normailsed, i.e. unit quaternion.

    Args:
        theta (float): angle of rotation
        vec (tuple/array): axis vector

    Example:
        90 degree rotation about the x axis:

            rotation_quaternion = Quaternion.q_angle_from_axis(np.pi/2, (1, 0, 0) )

    Returns:
        Quaternion: rotation quaternion
    """
    vec = Quaternion._normalise_axis(vec)
    w = _np.cos(theta / 2.0)
    vec *= _np.sin(theta / 2.0)
    x = vec[0]
    y = vec[1]
    z = vec[2]
    rotation_quaternion = Quaternion(w, x, y, z)
    return rotation_quaternion

vec_norm(x, y, z) staticmethod

Normalises each x,y,z vector

Parameters:

Name Type Description Default
x ndarray

x array

required
y ndarray

y array

required
z ndarray

z array

required

Returns:

Type Description
array

3xN array of normalised vectors

Source code in pymagnet/utils/_quaternion.py
@staticmethod
def vec_norm(x, y, z):
    """Normalises each x,y,z vector

    Args:
        x (ndarray): x array
        y (ndarray): y array
        z (ndarray): z array

    Returns:
        array: 3xN array of normalised vectors
    """
    vec = Quaternion._prepare_vector(x, y, z)
    return _np.linalg.norm(vec, axis=0)

Routines for Two Dimensional Magnet Classes

FgradB_2D(B, x, y, chi_m, c)

Calculates the magnetic field gradient force for a 2D field.

Parameters:

Name Type Description Default
B Field2

Magnetic field vector

required
x ndarray

x coordinates

required
y ndarray

y coordinates

required

Returns:

Type Description
Field2

Magnetic field gradient force vector

Source code in pymagnet/utils/_routines2D.py
def FgradB_2D(B, x, y, chi_m, c):
    """Calculates the magnetic field gradient force for a 2D field.

    Args:
        B (Field2): Magnetic field vector
        x (ndarray): x coordinates
        y (ndarray): y coordinates

    Returns:
        Field2: Magnetic field gradient force vector
    """

    BgB = Field2(_np.zeros_like(B.n), _np.zeros_like(B.n))
    FB = Field2(_np.zeros_like(B.n), _np.zeros_like(B.n))

    dB = gradB_2D(B, x, y)
    BgB.n = dB.n * B.n
    BgB.x = dB.x * B.n
    BgB.y = dB.y * B.n
    FB.n = (1 / MU0) * chi_m * c * BgB.n
    FB.x = (1 / MU0) * chi_m * c * BgB.x
    FB.y = (1 / MU0) * chi_m * c * BgB.y
    return FB

get_field_2D(Point_Array2)

Calculates magnetic field at an array of points due to every instantated Magnet2D magnet.

Parameters:

Name Type Description Default
Point_Array2 Point_Array2

array of x,y points and associated unit, defaults to 'mm'

required

Returns:

Type Description
Field2

array of Bx,By,|B| values and associated unit (defaults to 'T')

Source code in pymagnet/utils/_routines2D.py
def get_field_2D(Point_Array2):
    """Calculates magnetic field at an array of points due to every instantated
    `Magnet2D` magnet.

    Args:
        Point_Array2 (Point_Array2): array of x,y points and associated unit, defaults to 'mm'

    Returns:
        Field2: array of Bx,By,|B| values and associated unit (defaults to 'T')
    """
    from ..magnets import Magnet2D

    # Empty data structure
    B = _allocate_field_array2(Point_Array2.x, Point_Array2.y)

    for magnet in Magnet2D.instances:
        Bx, By = magnet.get_field(Point_Array2.x, Point_Array2.y)
        B.x += Bx
        B.y += By

    B.calc_norm()
    return B

gradB_2D(B, x, y)

Calculates the magnetic field gradient for a 2D field.

Parameters:

Name Type Description Default
B Field2

Magnetic field vector

required
x ndarray

x coordinates

required
y ndarray

y coordinates

required

Returns:

Type Description
Field2

Magnetic field gradient vector

Source code in pymagnet/utils/_routines2D.py
def gradB_2D(B, x, y):
    """Calculates the magnetic field gradient for a 2D field.

    Args:
        B (Field2): Magnetic field vector
        x (ndarray): x coordinates
        y (ndarray): y coordinates

    Returns:
        Field2: Magnetic field gradient vector
    """

    dB = Field2(_np.zeros_like(B), _np.zeros_like(B))
    Nx = x.shape[0]
    Ny = x.shape[1]
    dx = (x.max() - x.min()) / Nx
    dy = (y.max() - y.min()) / Ny
    dB.x, dB.y = _np.gradient(B, dx, dy)
    dB.calc_norm()
    return dB

grid2D(xmax, ymax, **kwargs)

Generates grid of x and y points

Parameters:

Name Type Description Default
xmax float

maximum x value

required
ymax float

maximum y value

required

Kwargs

num_points (int): Number of points in each direction. Defaults to 100 xmin (float): minimum x value. Defaults to -xmax ymin (float): minimum y value. Defaults to -ymax unit (str): unit length. Defaults to 'mm'

Returns:

Type Description
Point_Array2

array of x and y values of shape (num_points, num_points) and associated unit

Source code in pymagnet/utils/_routines2D.py
def grid2D(xmax, ymax, **kwargs):
    """Generates grid of x and y points

    Args:
        xmax (float): maximum x value
        ymax (float): maximum y value

    Kwargs:
        num_points (int): Number of points in each direction. Defaults to 100
        xmin (float): minimum x value. Defaults to -xmax
        ymin (float): minimum y value. Defaults to -ymax
        unit (str): unit length. Defaults to 'mm'

    Returns:
        Point_Array2: array of x and y values of shape (num_points, num_points) and associated unit
    """
    num_points = kwargs.pop("num_points", 100)
    xmin = kwargs.pop("xmin", -1 * xmax)
    ymin = kwargs.pop("ymin", -1 * ymax)
    unit = kwargs.pop("unit", "mm")
    NPJ = num_points * 1j
    x, y = _np.mgrid[xmin:xmax:NPJ, ymin:ymax:NPJ]
    return Point_Array2(x, y, unit=unit)

rotate_points_2D(x, y, alpha)

Counter-clockwise rotation of points x,y

Rotates 2D coordinates using a rotation matrix

Parameters:

Name Type Description Default
x ndarray

array of x coordinates

required
y ndarray

array of x coordinates

required
alpha float

rotation angle w.r.t. x-axis

required

Returns:

Type Description
tuple

(x', y') rotated array of points

Source code in pymagnet/utils/_routines2D.py
def rotate_points_2D(x, y, alpha):
    """Counter-clockwise rotation of points x,y

    Rotates 2D coordinates using a rotation matrix

    Args:
        x (ndarray): array of x coordinates
        y (ndarray): array of x coordinates
        alpha (float): rotation angle w.r.t. x-axis

    Returns:
        tuple: (x', y') rotated array of points
    """
    x = _np.atleast_1d(x)
    y = _np.atleast_1d(y)
    if len(x) != len(y):
        raise Exception("Must have same number of points in x and y")

    rot_matrix = _np.array(
        [[_np.cos(alpha), -_np.sin(alpha)], [_np.sin(alpha), _np.cos(alpha)]]
    )
    stacked_points = _np.column_stack((_np.ravel(x), _np.ravel(y)))
    rotated_points = _np.dot(rot_matrix, stacked_points.T)
    x_rotated = rotated_points[0, :]
    y_rotated = rotated_points[1, :]

    return _np.reshape(x_rotated, x.shape), _np.reshape(y_rotated, y.shape)

Routines for Three Dimensional Magnet Classes

get_field_3D(points)

Calculates magnetic field at an array of points due to every instantated Magnet3D magnet.

Parameters:

Name Type Description Default
Point_Array3 Point_Array3

array of x,y,z points and associated unit, defaults to 'mm'

required

Returns:

Type Description
Field3

array of Bx,By,Bz,|B| values and associated unit (defaults to 'T')

Source code in pymagnet/utils/_routines3D.py
def get_field_3D(points):
    """Calculates magnetic field at an array of points due to every instantated
    `Magnet3D` magnet.

    Args:
        Point_Array3 (Point_Array3): array of x,y,z points and associated unit, defaults to 'mm'

    Returns:
        Field3: array of Bx,By,Bz,|B| values and associated unit (defaults to 'T')
    """
    from ..magnets import Magnet3D

    B = _allocate_field_array3(points.x, points.y, points.z)

    for magnet in Magnet3D.instances:
        Bx, By, Bz = magnet.get_field(points.x, points.y, points.z)
        B.x += Bx.reshape(B.x.shape)
        B.y += By.reshape(B.y.shape)
        B.z += Bz.reshape(B.z.shape)

    B.calc_norm()
    return B

grid3D(xmax, ymax, zmax, **kwargs)

Generates grid of x, y, z points

Parameters:

Name Type Description Default
xmax float

maximum x value

required
ymax float

maximum y value

required
zmax float

maximum y value

required

Kwargs

num_points (int): Number of points in each direction. Defaults to 100 xmin (float): minimum x value. Defaults to -xmax ymin (float): minimum y value. Defaults to -ymax zmin (float): minimum y value. Defaults to -zmax unit (string): unit length. Defaults to 'mm'

Returns:

Type Description
Point_Array2

array of x and y values of shape (num_points, num_points) and associated unit

Source code in pymagnet/utils/_routines3D.py
def grid3D(xmax, ymax, zmax, **kwargs):
    """Generates grid of x, y, z points

    Args:
        xmax (float): maximum x value
        ymax (float): maximum y value
        zmax (float): maximum y value

    Kwargs:
        num_points (int): Number of points in each direction. Defaults to 100
        xmin (float): minimum x value. Defaults to -xmax
        ymin (float): minimum y value. Defaults to -ymax
        zmin (float): minimum y value. Defaults to -zmax
        unit (string): unit length. Defaults to 'mm'

    Returns:
        Point_Array2: array of x and y values of shape (num_points, num_points) and associated unit
    """
    num_points = kwargs.pop("num_points", None)

    xmin = kwargs.pop("xmin", -1 * xmax)
    ymin = kwargs.pop("ymin", -1 * ymax)
    zmin = kwargs.pop("zmin", -1 * zmax)
    unit = kwargs.pop("unit", "mm")
    # NPJ = num_points * 1j

    if num_points is None:
        num_points_x = kwargs.pop("num_points_x", 100)
        num_points_y = kwargs.pop("num_points_y", 100)
        num_points_z = kwargs.pop("num_points_z", 100)
    else:
        num_points_x = num_points
        num_points_y = num_points
        num_points_z = num_points

    x, y, z = _np.mgrid[
        xmin : xmax : num_points_x * 1j,
        ymin : ymax : num_points_y * 1j,
        zmin : zmax : num_points_z * 1j,
    ]

    return Point_Array3(x, y, z, unit=unit)

line3D(start, end, num_points=100, **kwargs)

Generates a line of points

Parameters:

Name Type Description Default
start tuple

Starting point (x1,y1,z1)

required
end tuple

End point (x2,y2,z2)

required
num_points int

number of points to generate. Defaults to 100

100
unit str

length scale units. Defaults to "mm".

required

Returns:

Type Description
Point_Array3

array of x, y, and z values of shape (num_points) and associated unit

Source code in pymagnet/utils/_routines3D.py
def line3D(start, end, num_points=100, **kwargs):
    """Generates a line of points

    Args:
        start (tuple): Starting point (x1,y1,z1)
        end (tuple): End point (x2,y2,z2)
        num_points (int): number of points to generate. Defaults to 100
        unit (str, optional): length scale units. Defaults to "mm".

    Returns:
        Point_Array3: array of x, y, and z values of shape (num_points) and associated unit
    """

    unit = kwargs.pop("unit", "mm")

    return Point_Array3(
        x=_np.linspace(start[0], end[0], num_points),
        y=_np.linspace(start[1], end[1], num_points),
        z=_np.linspace(start[2], end[2], num_points),
        unit=unit,
    )

plane3D(origin, point_a, point_b, num_points=100)

Generates a plane of points defined by

Parameters:

Name Type Description Default
origin tuple

Origin of plane

required
point_a tuple

Point defining end of vector in x-direction

required
point_b tuple

Point defining end of vector in y-direction

required
num_points int

Number of points in each direction, for a total of num_points^2. Defaults to 100.

100

Returns:

Type Description
Point_Array3

Struct containing the x,y,z coordinates

Source code in pymagnet/utils/_routines3D.py
def plane3D(origin, point_a, point_b, num_points=100):
    """Generates a plane of points defined by

    Args:
        origin (tuple): Origin of plane
        point_a (tuple): Point defining end of vector in x-direction
        point_b (tuple): Point defining end of vector in y-direction
        num_points (int, optional): Number of points in each direction, for a total of num_points^2. Defaults to 100.

    Returns:
        Point_Array3: Struct containing the x,y,z coordinates
    """

    vector_a = point_a - origin
    vector_b = point_b - origin
    num_points_j = num_points * 1j
    x_elements, y_elements = _np.mgrid[0:1:num_points_j, 0:1:num_points_j]
    x = origin[0] + x_elements * vector_a[0] + y_elements * vector_b[0]
    y = origin[1] + x_elements * vector_a[1] + y_elements * vector_b[1]
    z = origin[2] + x_elements * vector_a[2] + y_elements * vector_b[2]

    return Point_Array3(x=x, y=y, z=z)

point3D(point, **kwargs)

Returns a single point

Parameters:

Name Type Description Default
point tuple required
unit str

length scale units. Defaults to "mm".

required

Returns:

Type Description
Point_Array3

struct of x, y, and z values of shape (1) and associated unit

Source code in pymagnet/utils/_routines3D.py
def point3D(point, **kwargs):
    """Returns a single point

    Args:
        point (tuple):
        unit (str, optional): length scale units. Defaults to "mm".

    Returns:
        Point_Array3: struct of x, y, and z values of shape (1) and associated unit
    """

    unit = kwargs.pop("unit", "mm")

    return Point_Array3(
        x=point[0],
        y=point[1],
        z=point[2],
        unit=unit,
    )

slice3D(plane='xy', max1=1.0, max2=1.0, slice_value=0.0, unit='mm', **kwargs)

Generates a planar slice of values

Parameters:

Name Type Description Default
plane str

plane. Defaults to "xy".

'xy'
max1 float

maximum along axis 1. Defaults to 1.0.

1.0
max2 float

maximum along axis 2. Defaults to 1.0.

1.0
slice_value float

constant value for third axis. Defaults to 0.0.

0.0
unit str

length scale units. Defaults to "mm".

'mm'

Kwargs

num_points (int): Number of points in each direction. Defaults to 100 min1 (float): minimum along axis 1. Defaults to -min1 min2 (float): minimum along axis 2. Defaults to -min2

Exceptions:

Type Description
Exception

plane type, must be one of 'xy', 'xz, 'yz', or 'custom'

Returns:

Type Description
Point_Array3

array of x, y, and z values of shape (num_points, num_points) and associated unit

Source code in pymagnet/utils/_routines3D.py
def slice3D(plane="xy", max1=1.0, max2=1.0, slice_value=0.0, unit="mm", **kwargs):
    """Generates a planar slice of values

    Args:
        plane (str, optional): plane. Defaults to "xy".
        max1 (float, optional): maximum along axis 1. Defaults to 1.0.
        max2 (float, optional): maximum along axis 2. Defaults to 1.0.
        slice_value (float, optional): constant value for third axis. Defaults to 0.0.
        unit (str, optional): length scale units. Defaults to "mm".

    Kwargs:
        num_points (int): Number of points in each direction. Defaults to 100
        min1 (float): minimum along axis 1. Defaults to -min1
        min2 (float): minimum along axis 2. Defaults to -min2

    Raises:
        Exception: plane type, must be one of 'xy', 'xz, 'yz', or 'custom'

    Returns:
        Point_Array3: array of x, y, and z values of shape (num_points, num_points) and associated unit
    """
    num_points = kwargs.pop("num_points", 100)
    min1 = kwargs.pop("min1", -1 * max1)
    min2 = kwargs.pop("min2", -1 * max2)
    NPj = num_points * 1j

    if plane.lower() == "xy":
        x, y = _np.mgrid[min1:max1:NPj, min2:max2:NPj]
        z = _np.asarray([slice_value])
        z = _np.tile(z, x.shape)

    elif plane.lower() == "xz":
        x, z = _np.mgrid[min1:max1:NPj, min2:max2:NPj]
        y = _np.asarray([slice_value])
        y = _np.tile(y, x.shape)

    elif plane.lower() == "yz":
        y, z = _np.mgrid[min1:max1:NPj, min2:max2:NPj]
        x = _np.asarray([slice_value])
        x = _np.tile(x, y.shape)

    elif plane.lower() == "custom":
        x = kwargs.pop("custom_x", _np.array([0.0]))
        y = kwargs.pop("custom_y", _np.array([0.0]))
        z = kwargs.pop("custom_z", _np.array([0.0]))

    else:
        raise Exception("plane must be one of 'xy', 'xz, 'yz', or 'custom'")

    return Point_Array3(x, y, z, unit=unit)

Contains functions needed to rotate and translate a triangle to lie in the xz plane and to divide it into two right angled triangles

align_triangle_to_y(triangle, rot_axis, norm_vec)

Rotates and translates a triangle in lie in the xz plane

Parameters:

Name Type Description Default
triangle ndarray

vertices of a triangle

required
rot_axis ndarray

axis about which to rotate triangle

required
norm_vec ndarray

normal to triangle

required

Returns:

Type Description
tuple

aligned_triangle (ndarray, rotated triangle), first_rotation (quaternion, align to y-axis)

Source code in pymagnet/utils/_trigonometry3D.py
def align_triangle_to_y(triangle, rot_axis, norm_vec):
    """Rotates and translates a triangle in lie in the xz plane

    Args:
        triangle (ndarray): vertices of a triangle
        rot_axis (ndarray): axis about which to rotate triangle
        norm_vec (ndarray): normal to triangle

    Returns:
        tuple: aligned_triangle (ndarray, rotated triangle), first_rotation (quaternion, align to y-axis)
    """
    y_axis = _np.array([0, 1, 0])

    if _np.linalg.norm(rot_axis) < ALIGN_CUTOFF:

        # Check if parallel or anti-parallel
        if check_sign(y_axis, norm_vec):
            # Parallel
            first_rotation = Quaternion()
            aligned_triangle = triangle

        else:
            # Anti-parallel
            first_rotation = Quaternion.q_angle_from_axis(PI, y_axis)
            aligned_triangle = rotate_points(triangle, first_rotation)

    else:
        angle = -_np.arccos(_np.dot(y_axis, norm_vec))
        first_rotation = Quaternion.q_angle_from_axis(angle, rot_axis)
        aligned_triangle = rotate_points(triangle, first_rotation)
    return aligned_triangle, first_rotation

align_triangle_xz(triangle, longest_side)

Returns quaternions needed to rotate a triangle lying in the xz plane to be aligned with its longest side along x and altitude along z

Parameters:

Name Type Description Default
triangle ndarray

vertices of triangle

required
longest_side int

index of the longest side of triangle

required

Returns:

Type Description
tuple

second_rotation (quaternion, align to x-axis), third_rotation (quaternion, align to z-axis):

Source code in pymagnet/utils/_trigonometry3D.py
def align_triangle_xz(triangle, longest_side):
    """Returns quaternions needed to rotate a triangle lying in the xz plane to be
    aligned with its longest side along x and altitude along z

    Args:
        triangle (ndarray): vertices of triangle
        longest_side (int): index of the longest side of triangle

    Returns:
        tuple: second_rotation (quaternion, align to x-axis), third_rotation (quaternion, align to z-axis):
    """
    x_axis = _np.array([1, 0, 0])
    y_axis = _np.array([0, 1, 0])
    z_axis = _np.array([0, 0, 1])

    # side_list = [0, 1, 2]
    # side_list.pop(longest_side)

    vec_x = return_axis_vector(triangle, longest_side)
    rot_axis = _np.cross(x_axis, vec_x)

    # Check aligment of base of triangle with x-axis
    if _np.linalg.norm(rot_axis) < ALIGN_CUTOFF:

        # Check if parallel or anti-parallel
        if check_sign(x_axis, vec_x):
            # Parallel
            second_rotation = Quaternion()
            tri_x = triangle
        else:
            # Anti-parallel
            second_rotation = Quaternion.q_angle_from_axis(PI, y_axis)
            tri_x = rotate_points(triangle, second_rotation)

    else:
        angle = -_np.arccos(_np.dot(x_axis, vec_x))
        second_rotation = Quaternion.q_angle_from_axis(angle, rot_axis)
        tri_x = rotate_points(triangle, second_rotation)

    vec_z = return_z_vector(tri_x, longest_side)
    rot_axis = _np.cross(z_axis, vec_z)

    # Check aligment of triangle altitude with z-axis
    if _np.all(_np.fabs([rot_axis]) < ALIGN_CUTOFF):

        # Check if parallel anti-parallel
        if check_sign(z_axis, vec_z):
            # Parallel
            third_rotation = Quaternion()
        else:
            # Anti-parallel
            third_rotation = Quaternion.q_angle_from_axis(PI, y_axis)

    else:
        angle = -_np.arccos(_np.dot(z_axis, vec_z))
        third_rotation = Quaternion.q_angle_from_axis(angle, rot_axis)

    return second_rotation, third_rotation

altitude(a, b, c)

Gets altitude to side a of a triangle.

Parameters:

Name Type Description Default
a float

longest side

required
b float

triangle side

required
c float

triangle side

required

Returns:

Type Description
float

altitude to side a

Source code in pymagnet/utils/_trigonometry3D.py
def altitude(a, b, c):
    """Gets altitude to side `a` of a triangle.

    Args:
        a (float): longest side
        b (float): triangle side
        c (float): triangle side

    Returns:
        float: altitude to side `a`
    """
    s = (a + b + c) / 2
    return 2 * _np.sqrt(s * (s - a) * (s - b) * (s - c)) / a

check_sign(vector_1, vector_2)

Returns true if the signs of all elements of two arrays are the same

Parameters:

Name Type Description Default
vector_1 ndarray

input array 2

required
vector_2 ndarray

input array 2

required

Returns:

Type Description
boolean

True if elements in two arrays have the same sign

Source code in pymagnet/utils/_trigonometry3D.py
def check_sign(vector_1, vector_2):
    """Returns true if the signs of all elements of two arrays are the same

    Args:
        vector_1 (ndarray): input array 2
        vector_2 (ndarray): input array 2

    Returns:
        boolean: True if elements in two arrays have the same sign
    """
    sign_comp_1 = _np.fabs(vector_1 + vector_2)
    sign_comp_2 = _np.fabs(vector_1) + _np.fabs(vector_2)

    return _np.allclose(sign_comp_1, sign_comp_2, atol=1e-6)

norm_plane(vec)

Calculates the normal to a triangular plane

Parameters:

Name Type Description Default
vec ndarray/list/tuple

(N,1) array

required

Returns:

Type Description
ndarray

normal vector (N,)

Source code in pymagnet/utils/_trigonometry3D.py
def norm_plane(vec):
    """Calculates the normal to a triangular plane

    Args:
        vec (ndarray/list/tuple): (N,1) array

    Returns:
        ndarray: normal vector (N,)
    """
    norm = _np.cross(vec[1] - vec[0], vec[2] - vec[0])
    norm = norm / _np.linalg.norm(norm)
    return norm

return_axis_vector(triangle, longest_side)

Returns vector collinear to the longest side of a triangle

Parameters:

Name Type Description Default
triangle ndarray

triangle vertices

required
longest_side int

longest side index

required

Returns:

Type Description
ndarray

vector corresponding to longest side

Source code in pymagnet/utils/_trigonometry3D.py
def return_axis_vector(triangle, longest_side):
    """Returns vector collinear to the longest side of a triangle

    Args:
        triangle (ndarray): triangle vertices
        longest_side (int): longest side index

    Returns:
        ndarray: vector corresponding to longest side
    """
    vector_A = triangle[1] - triangle[0]
    vector_A = vector_A / _np.linalg.norm(vector_A)

    vector_B = triangle[2] - triangle[1]
    vector_B = vector_B / _np.linalg.norm(vector_B)
    vector_C = triangle[2] - triangle[0]
    vector_C = vector_C / _np.linalg.norm(vector_C)

    vec_dict = {
        0: vector_A,
        1: vector_B,
        2: vector_C,
    }

    vec = vec_dict[longest_side]

    return vec

return_z_vector(triangle, longest_side)

Returns altitude vector from longest side

Parameters:

Name Type Description Default
triangle ndarray

triangle vertices

required
longest_side int

longest side index

required

Returns:

Type Description
ndarray

altitude vector

Source code in pymagnet/utils/_trigonometry3D.py
def return_z_vector(triangle, longest_side):
    """Returns altitude vector from longest side

    Args:
        triangle (ndarray): triangle vertices
        longest_side (int): longest side index

    Returns:
        ndarray: altitude vector
    """
    new_vertex_dict = {
        0: [triangle[2, 0], triangle[0, 1], triangle[0, 2]],
        1: [triangle[0, 0], triangle[0, 1], triangle[1, 2]],
        2: [triangle[1, 0], triangle[2, 1], triangle[2, 2]],
    }
    new_vertex = _np.array(new_vertex_dict[longest_side])
    vec_z_dict = {
        0: triangle[2] - new_vertex,
        1: triangle[0] - new_vertex,
        2: triangle[1] - new_vertex,
    }
    vec_z = vec_z_dict[longest_side]
    vec_z = vec_z / _np.linalg.norm(vec_z)

    return vec_z

rotate_points(points, rotation_quaternion)

Rotates a set of points

Parameters:

Name Type Description Default
points [type]

[description]

required
rotation_quaternion [type]

[description]

required

Returns:

Type Description
[type]

[description]

Source code in pymagnet/utils/_trigonometry3D.py
def rotate_points(points, rotation_quaternion):
    """Rotates a set of points

    Args:
        points ([type]): [description]
        rotation_quaternion ([type]): [description]

    Returns:
        [type]: [description]
    """
    x_rot, y_rot, z_rot = rotation_quaternion * points.T
    rotate_points = _np.vstack([x_rot, y_rot, z_rot]).T

    return rotate_points

signed_area(triangle)

Calculates signed area of a triangle. Area area < 0 for clockwise ordering. Assumes the triangle is in the xz plane (i.e. with the normal parallel to y).

Parameters:

Name Type Description Default
triangle ndarray

3x3 array of vertices

required

Returns:

Type Description
float

signed area

Source code in pymagnet/utils/_trigonometry3D.py
@jit
def signed_area(triangle):
    """Calculates signed area of a triangle. Area area < 0 for clockwise ordering.
    Assumes the triangle is in the xz plane (i.e. with the normal parallel to y).

    Args:
        triangle (ndarray): 3x3 array of vertices

    Returns:
        float: signed area
    """

    j = 1
    NP = 3
    area = 0.0

    for i in range(NP):
        j = j % NP

        area += (triangle[j][0] - triangle[i][0]) * (triangle[j][2] + triangle[i][2])
        j += 1

    # check winding order of polygon, area < 0 for clockwise ordering of points
    area /= 2.0

    return area

pymagnets.utils._vector_structs

Private module consiting of vector and point array classes and their methods.

Field1 (Point_Array1)

1D Field vector This is used to contain one component (Bz as z), and the units ('T', 'mT', etc)

Source code in pymagnet/utils/_vector_structs.py
class Field1(Point_Array1):
    """1D Field vector
    This is used to contain one component (Bz as z), and the units
    ('T', 'mT', etc)
    """

    def __init__(self, z, unit="T"):
        """Init method

        Args:
            z (ndarray): Magnetic field component
            unit (str, optional): Unit of field. Defaults to "T".

        Raises:
            ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
        """
        super().__init__(z)
        if get_unit_value_tesla(unit) is not None:
            self.unit = unit
        else:
            raise ValueError("Error, not an SI prefix, e.g. T, mT, uT, nT ")

    def __repr__(self) -> str:
        return f"[Unit: {self.unit}\nBz: {self.z}]"

    def __str__(self) -> str:
        return f"[Unit: {self.unit}\nBz: {self.z}]"

    def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
        """Converts field array to a different unit. e.g from 'T' to 'mT'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
        """
        super().change_unit(new_unit, get_unit_value)

__init__(self, z, unit='T') special

Init method

Parameters:

Name Type Description Default
z ndarray

Magnetic field component

required
unit str

Unit of field. Defaults to "T".

'T'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. T, mT, uT, nT

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, z, unit="T"):
    """Init method

    Args:
        z (ndarray): Magnetic field component
        unit (str, optional): Unit of field. Defaults to "T".

    Raises:
        ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
    """
    super().__init__(z)
    if get_unit_value_tesla(unit) is not None:
        self.unit = unit
    else:
        raise ValueError("Error, not an SI prefix, e.g. T, mT, uT, nT ")

change_unit(self, new_unit, get_unit_value=<function get_unit_value_tesla at 0x103a79280>)

Converts field array to a different unit. e.g from 'T' to 'mT'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_tesla.

<function get_unit_value_tesla at 0x103a79280>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
    """Converts field array to a different unit. e.g from 'T' to 'mT'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
    """
    super().change_unit(new_unit, get_unit_value)

Field2 (Point_Array2)

2D Field vector This is used to contain two components (x, y), and the units ('T', 'mT', etc)

Source code in pymagnet/utils/_vector_structs.py
class Field2(Point_Array2):
    """2D Field vector
    This is used to contain two components (x, y), and the units
    ('T', 'mT', etc)
    """

    def __init__(self, x, y, unit="T"):
        """Init method

        Args:
            x (ndarray): Magnetic field component Bx
            y (ndarray): Magnetic field component By
            unit (str, optional): Unit of field. Defaults to "T".

        Raises:
            ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
        """
        super().__init__(x, y)
        self.n = _np.zeros_like(x)
        if get_unit_value_tesla(unit) is not None:
            self.unit = unit
        else:
            raise ValueError("Error, not an SI prefix, e.g. T, mT, uT, nT ")

    def calc_norm(self):
        """Calculates the norm of the 2D vector"""
        self.n = _np.linalg.norm([self.x, self.y], axis=0)

    def __repr__(self) -> str:
        return f"[Unit: {self.unit}\nBx: {self.x}\nBy: {self.y}\nBn: {self.n}]"

    def __str__(self) -> str:
        return f"[Unit: {self.unit}\nBx: {self.x}\nBy: {self.y}\nBn: {self.n}]"

    def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
        """Converts field array to a different unit. e.g from 'T' to 'mT'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
        """
        super().change_unit(new_unit, get_unit_value)

__init__(self, x, y, unit='T') special

Init method

Parameters:

Name Type Description Default
x ndarray

Magnetic field component Bx

required
y ndarray

Magnetic field component By

required
unit str

Unit of field. Defaults to "T".

'T'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. T, mT, uT, nT

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, x, y, unit="T"):
    """Init method

    Args:
        x (ndarray): Magnetic field component Bx
        y (ndarray): Magnetic field component By
        unit (str, optional): Unit of field. Defaults to "T".

    Raises:
        ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
    """
    super().__init__(x, y)
    self.n = _np.zeros_like(x)
    if get_unit_value_tesla(unit) is not None:
        self.unit = unit
    else:
        raise ValueError("Error, not an SI prefix, e.g. T, mT, uT, nT ")

calc_norm(self)

Calculates the norm of the 2D vector

Source code in pymagnet/utils/_vector_structs.py
def calc_norm(self):
    """Calculates the norm of the 2D vector"""
    self.n = _np.linalg.norm([self.x, self.y], axis=0)

change_unit(self, new_unit, get_unit_value=<function get_unit_value_tesla at 0x103a79280>)

Converts field array to a different unit. e.g from 'T' to 'mT'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_tesla.

<function get_unit_value_tesla at 0x103a79280>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
    """Converts field array to a different unit. e.g from 'T' to 'mT'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
    """
    super().change_unit(new_unit, get_unit_value)

Field3 (Point_Array3)

3D Field vector This is used to contain three components (x, y), and the units ('T', 'mT', etc)

Source code in pymagnet/utils/_vector_structs.py
class Field3(Point_Array3):
    """3D Field vector
    This is used to contain three components (x, y), and the units
    ('T', 'mT', etc)
    """

    def __init__(self, x, y, z, unit="T"):
        """Init method

        Args:
            x (ndarray): Magnetic field component Bx
            y (ndarray): Magnetic field component By
            z (ndarray): Magnetic field component Bz
            unit (str, optional): Unit of field. Defaults to "T".

        Raises:
            ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
        """
        super().__init__(x, y, z)
        self.n = _np.zeros_like(x)
        self.unit = unit

    def calc_norm(self):
        """Calculates the norm of the 3D vector"""
        self.n = _np.linalg.norm([self.x, self.y, self.z], axis=0)

    def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
        """Converts field array to a different unit. e.g from 'T' to 'mT'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
        """
        super().change_unit(new_unit, get_unit_value)

    def __repr__(self) -> str:
        return f"[Unit: {self.unit}\nBx: {self.x}\nBy: {self.y}\nBz: {self.z}\nBn: {self.n}]"

    def __str__(self) -> str:
        return f"[Unit: {self.unit}\nBx: {self.x}\nBy: {self.y}\nBz: {self.z}\nBn: {self.n}]"

__init__(self, x, y, z, unit='T') special

Init method

Parameters:

Name Type Description Default
x ndarray

Magnetic field component Bx

required
y ndarray

Magnetic field component By

required
z ndarray

Magnetic field component Bz

required
unit str

Unit of field. Defaults to "T".

'T'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. T, mT, uT, nT

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, x, y, z, unit="T"):
    """Init method

    Args:
        x (ndarray): Magnetic field component Bx
        y (ndarray): Magnetic field component By
        z (ndarray): Magnetic field component Bz
        unit (str, optional): Unit of field. Defaults to "T".

    Raises:
        ValueError: Unit must an SI prefix, e.g. T, mT, uT, nT
    """
    super().__init__(x, y, z)
    self.n = _np.zeros_like(x)
    self.unit = unit

calc_norm(self)

Calculates the norm of the 3D vector

Source code in pymagnet/utils/_vector_structs.py
def calc_norm(self):
    """Calculates the norm of the 3D vector"""
    self.n = _np.linalg.norm([self.x, self.y, self.z], axis=0)

change_unit(self, new_unit, get_unit_value=<function get_unit_value_tesla at 0x103a79280>)

Converts field array to a different unit. e.g from 'T' to 'mT'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_tesla.

<function get_unit_value_tesla at 0x103a79280>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_tesla):
    """Converts field array to a different unit. e.g from 'T' to 'mT'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_tesla.
    """
    super().change_unit(new_unit, get_unit_value)

Point_Array1

1D point structure This is used to contain one position array (z), and the units ('mm', 'cm', etc)

Source code in pymagnet/utils/_vector_structs.py
class Point_Array1(object):
    """1D point structure
    This is used to contain one position array (z), and the units
    ('mm', 'cm', etc)
    """

    def __init__(self, z, unit="mm"):
        """Init method

        Args:
            z (ndarray): z coordinates
            unit (str, optional): Unit of length. Defaults to "mm".

        Raises:
            ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
        """
        self.z = _np.asarray(z)
        if get_unit_value_meter(unit) is not None:
            self.unit = unit
        else:
            raise ValueError("Error, not an SI prefix, e.g. km, m, cm, mm ")

    def __repr__(self) -> str:
        return f"[(Unit:{self.unit}) Array:{self.z}]"

    def __str__(self) -> str:
        return f"[(Unit:{self.unit}) Array:{self.z}]"

    def get_unit(self):
        """Gets unit

        Returns:
            str: unit
        """
        return self.unit

    def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
        """Converts point array to a different unit. e.g from 'cm' to 'mm'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
        """
        from ..magnets import Cylinder, Magnet, Prism

        current_unit_val = get_unit_value(self.get_unit())
        new_unit_val = get_unit_value(new_unit)
        scale_val = current_unit_val / new_unit_val

        self.z *= scale_val

        self.unit = new_unit
        for magnet in Magnet.instances:
            magnet.center = scale_val * magnet.center
            if issubclass(magnet.__class__, Prism):
                magnet.a = magnet.a * scale_val
                magnet.b = magnet.b * scale_val
                magnet.c = magnet.c * scale_val
                magnet.width = magnet.width * scale_val
                magnet.depth = magnet.depth * scale_val
                magnet.height = magnet.height * scale_val
            elif issubclass(magnet.__class__, Cylinder):
                magnet.radius = magnet.radius * scale_val
                magnet.length = magnet.length * scale_val

__init__(self, z, unit='mm') special

Init method

Parameters:

Name Type Description Default
z ndarray

z coordinates

required
unit str

Unit of length. Defaults to "mm".

'mm'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. km, m, cm, mm

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, z, unit="mm"):
    """Init method

    Args:
        z (ndarray): z coordinates
        unit (str, optional): Unit of length. Defaults to "mm".

    Raises:
        ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
    """
    self.z = _np.asarray(z)
    if get_unit_value_meter(unit) is not None:
        self.unit = unit
    else:
        raise ValueError("Error, not an SI prefix, e.g. km, m, cm, mm ")

change_unit(self, new_unit, get_unit_value=<function get_unit_value_meter at 0x103a791f0>)

Converts point array to a different unit. e.g from 'cm' to 'mm'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_meter.

<function get_unit_value_meter at 0x103a791f0>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
    """Converts point array to a different unit. e.g from 'cm' to 'mm'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
    """
    from ..magnets import Cylinder, Magnet, Prism

    current_unit_val = get_unit_value(self.get_unit())
    new_unit_val = get_unit_value(new_unit)
    scale_val = current_unit_val / new_unit_val

    self.z *= scale_val

    self.unit = new_unit
    for magnet in Magnet.instances:
        magnet.center = scale_val * magnet.center
        if issubclass(magnet.__class__, Prism):
            magnet.a = magnet.a * scale_val
            magnet.b = magnet.b * scale_val
            magnet.c = magnet.c * scale_val
            magnet.width = magnet.width * scale_val
            magnet.depth = magnet.depth * scale_val
            magnet.height = magnet.height * scale_val
        elif issubclass(magnet.__class__, Cylinder):
            magnet.radius = magnet.radius * scale_val
            magnet.length = magnet.length * scale_val

get_unit(self)

Gets unit

Returns:

Type Description
str

unit

Source code in pymagnet/utils/_vector_structs.py
def get_unit(self):
    """Gets unit

    Returns:
        str: unit
    """
    return self.unit

Point_Array2

2D point structure This is used to contain two position arrays (x, y), and the units ('mm', 'cm', etc)

Source code in pymagnet/utils/_vector_structs.py
class Point_Array2(object):
    """2D point structure
    This is used to contain two position arrays (x, y), and the units
    ('mm', 'cm', etc)
    """

    def __init__(self, x, y, unit="mm"):
        """Init Method

        Args:
            x (ndarray): x coordinates
            y (ndarray): y coordinates
            unit (str, optional): Unit of length. Defaults to "mm".

        Raises:
            ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
        """
        self.x = _np.asarray(x)
        self.y = _np.asarray(y)
        if get_unit_value_meter(unit) is not None:
            self.unit = unit
        else:
            raise ValueError("Error, not an SI prefix, e.g. km, m, cm, mm ")

    def __repr__(self) -> str:
        return f"[Unit: {self.unit}\nx: {self.x}\ny: {self.y}]"

    def __str__(self) -> str:
        return f"[Unit: {self.unit}\nx: {self.x}\ny: {self.y}]"

    def get_unit(self):
        return self.unit

    def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
        """Converts point array to a different unit. e.g from 'cm' to 'mm'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
        """
        from ..magnets import Circle, Magnet, PolyMagnet, Rectangle, Square

        current_unit_val = get_unit_value(self.get_unit())
        new_unit_val = get_unit_value(new_unit)
        scale_val = current_unit_val / new_unit_val

        self.x *= scale_val
        self.y *= scale_val
        self.unit = new_unit
        for magnet in Magnet.instances:
            magnet.center = scale_val * magnet.center
            if issubclass(magnet.__class__, Rectangle):
                magnet.a = magnet.a * scale_val
                magnet.b = magnet.b * scale_val
                magnet.width = magnet.width * scale_val
                magnet.height = magnet.height * scale_val
            elif issubclass(magnet.__class__, Square):
                magnet.a = magnet.a * scale_val
                magnet.width = magnet.width * scale_val
            elif issubclass(magnet.__class__, Circle):
                magnet.radius = magnet.radius * scale_val
            elif issubclass(magnet.__class__, PolyMagnet):
                magnet.polygon.vertices = (
                    scale_val * _np.array(magnet.polygon.vertices)
                ).tolist()

__init__(self, x, y, unit='mm') special

Init Method

Parameters:

Name Type Description Default
x ndarray

x coordinates

required
y ndarray

y coordinates

required
unit str

Unit of length. Defaults to "mm".

'mm'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. km, m, cm, mm

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, x, y, unit="mm"):
    """Init Method

    Args:
        x (ndarray): x coordinates
        y (ndarray): y coordinates
        unit (str, optional): Unit of length. Defaults to "mm".

    Raises:
        ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
    """
    self.x = _np.asarray(x)
    self.y = _np.asarray(y)
    if get_unit_value_meter(unit) is not None:
        self.unit = unit
    else:
        raise ValueError("Error, not an SI prefix, e.g. km, m, cm, mm ")

change_unit(self, new_unit, get_unit_value=<function get_unit_value_meter at 0x103a791f0>)

Converts point array to a different unit. e.g from 'cm' to 'mm'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_meter.

<function get_unit_value_meter at 0x103a791f0>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
    """Converts point array to a different unit. e.g from 'cm' to 'mm'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
    """
    from ..magnets import Circle, Magnet, PolyMagnet, Rectangle, Square

    current_unit_val = get_unit_value(self.get_unit())
    new_unit_val = get_unit_value(new_unit)
    scale_val = current_unit_val / new_unit_val

    self.x *= scale_val
    self.y *= scale_val
    self.unit = new_unit
    for magnet in Magnet.instances:
        magnet.center = scale_val * magnet.center
        if issubclass(magnet.__class__, Rectangle):
            magnet.a = magnet.a * scale_val
            magnet.b = magnet.b * scale_val
            magnet.width = magnet.width * scale_val
            magnet.height = magnet.height * scale_val
        elif issubclass(magnet.__class__, Square):
            magnet.a = magnet.a * scale_val
            magnet.width = magnet.width * scale_val
        elif issubclass(magnet.__class__, Circle):
            magnet.radius = magnet.radius * scale_val
        elif issubclass(magnet.__class__, PolyMagnet):
            magnet.polygon.vertices = (
                scale_val * _np.array(magnet.polygon.vertices)
            ).tolist()

Point_Array3 (Point_Array2)

3D point structure This is used to contain three position arrays (x, y, z), and the units ('mm', 'cm', etc)

Source code in pymagnet/utils/_vector_structs.py
class Point_Array3(Point_Array2):
    """3D point structure
    This is used to contain three position arrays (x, y, z), and the units
    ('mm', 'cm', etc)
    """

    def __init__(self, x, y, z, unit="mm"):
        """Init Method

        Args:
            x (ndarray): x coordinates
            y (ndarray): y coordinates
            z (ndarray): z coordinates
            unit (str, optional): Unit of length. Defaults to "mm".

        Raises:
            ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
        """
        super().__init__(x, y, unit=unit)
        self.z = _np.asarray(z)

    def __repr__(self) -> str:
        return f"[Unit: {self.unit}\nx: {self.x}\ny: {self.y}\nz: {self.z}]"

    def __str__(self) -> str:
        return f"[Unit: {self.unit}\nx: {self.x}\ny: {self.y}\nz: {self.z}]"

    def rotate(self, alpha, beta, gamma):
        """Rotates set of Point_Array3 points by angles alpha, beta, gamma

        Args:
            alpha (float): Angle to rotate about z in degrees
            beta (float): Angle to rotate about y in degrees
            gamma (float): Angle to rotate about x in degrees

        Returns:
            Point_Array3: rotated set of points
        """
        q_fwd = Quaternion.gen_rotation_quaternion(
            _np.deg2rad(alpha), _np.deg2rad(beta), _np.deg2rad(gamma)
        )

        pos_vec = Quaternion._prepare_vector(self.x, self.y, self.z)
        x_rot, y_rot, z_rot = q_fwd * pos_vec
        return Point_Array3(
            x=x_rot.reshape(self.x.shape),
            y=y_rot.reshape(self.x.shape),
            z=z_rot.reshape(self.x.shape),
        )

    def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
        """Converts point array to a different unit. e.g from 'cm' to 'mm'

        Args:
            new_unit (str): unit to be converted to
            get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
        """
        from ..magnets import Cube, Cylinder, Magnet, Mesh, Prism, Sphere

        current_unit_val = get_unit_value(self.get_unit())
        new_unit_val = get_unit_value(new_unit)
        scale_val = current_unit_val / new_unit_val
        self.x *= scale_val
        self.y *= scale_val
        self.z *= scale_val
        self.unit = new_unit

        for magnet in Magnet.instances:
            magnet.center = scale_val * magnet.center
            if issubclass(magnet.__class__, Prism):
                magnet.a = magnet.a * scale_val
                magnet.b = magnet.b * scale_val
                magnet.c = magnet.c * scale_val
                magnet.width = magnet.width * scale_val
                magnet.height = magnet.height * scale_val
                magnet.depth = magnet.depth * scale_val
            elif issubclass(magnet.__class__, Cube):
                magnet.a = magnet.a * scale_val
                magnet.width = magnet.width * scale_val
            elif issubclass(magnet.__class__, Cylinder):
                magnet.radius = magnet.radius * scale_val
                magnet.length = magnet.length * scale_val
            elif issubclass(magnet.__class__, Sphere):
                magnet.radius = magnet.radius * scale_val
            elif issubclass(magnet.__class__, Mesh):
                magnet.mesh_vectors = magnet.mesh_vectors * scale_val

__init__(self, x, y, z, unit='mm') special

Init Method

Parameters:

Name Type Description Default
x ndarray

x coordinates

required
y ndarray

y coordinates

required
z ndarray

z coordinates

required
unit str

Unit of length. Defaults to "mm".

'mm'

Exceptions:

Type Description
ValueError

Unit must an SI prefix, e.g. km, m, cm, mm

Source code in pymagnet/utils/_vector_structs.py
def __init__(self, x, y, z, unit="mm"):
    """Init Method

    Args:
        x (ndarray): x coordinates
        y (ndarray): y coordinates
        z (ndarray): z coordinates
        unit (str, optional): Unit of length. Defaults to "mm".

    Raises:
        ValueError: Unit must an SI prefix, e.g. km, m, cm, mm
    """
    super().__init__(x, y, unit=unit)
    self.z = _np.asarray(z)

change_unit(self, new_unit, get_unit_value=<function get_unit_value_meter at 0x103a791f0>)

Converts point array to a different unit. e.g from 'cm' to 'mm'

Parameters:

Name Type Description Default
new_unit str

unit to be converted to

required
get_unit_value function

Function for checking unit type. Defaults to get_unit_value_meter.

<function get_unit_value_meter at 0x103a791f0>
Source code in pymagnet/utils/_vector_structs.py
def change_unit(self, new_unit, get_unit_value=get_unit_value_meter):
    """Converts point array to a different unit. e.g from 'cm' to 'mm'

    Args:
        new_unit (str): unit to be converted to
        get_unit_value (function, optional): Function for checking unit type. Defaults to get_unit_value_meter.
    """
    from ..magnets import Cube, Cylinder, Magnet, Mesh, Prism, Sphere

    current_unit_val = get_unit_value(self.get_unit())
    new_unit_val = get_unit_value(new_unit)
    scale_val = current_unit_val / new_unit_val
    self.x *= scale_val
    self.y *= scale_val
    self.z *= scale_val
    self.unit = new_unit

    for magnet in Magnet.instances:
        magnet.center = scale_val * magnet.center
        if issubclass(magnet.__class__, Prism):
            magnet.a = magnet.a * scale_val
            magnet.b = magnet.b * scale_val
            magnet.c = magnet.c * scale_val
            magnet.width = magnet.width * scale_val
            magnet.height = magnet.height * scale_val
            magnet.depth = magnet.depth * scale_val
        elif issubclass(magnet.__class__, Cube):
            magnet.a = magnet.a * scale_val
            magnet.width = magnet.width * scale_val
        elif issubclass(magnet.__class__, Cylinder):
            magnet.radius = magnet.radius * scale_val
            magnet.length = magnet.length * scale_val
        elif issubclass(magnet.__class__, Sphere):
            magnet.radius = magnet.radius * scale_val
        elif issubclass(magnet.__class__, Mesh):
            magnet.mesh_vectors = magnet.mesh_vectors * scale_val

rotate(self, alpha, beta, gamma)

Rotates set of Point_Array3 points by angles alpha, beta, gamma

Parameters:

Name Type Description Default
alpha float

Angle to rotate about z in degrees

required
beta float

Angle to rotate about y in degrees

required
gamma float

Angle to rotate about x in degrees

required

Returns:

Type Description
Point_Array3

rotated set of points

Source code in pymagnet/utils/_vector_structs.py
def rotate(self, alpha, beta, gamma):
    """Rotates set of Point_Array3 points by angles alpha, beta, gamma

    Args:
        alpha (float): Angle to rotate about z in degrees
        beta (float): Angle to rotate about y in degrees
        gamma (float): Angle to rotate about x in degrees

    Returns:
        Point_Array3: rotated set of points
    """
    q_fwd = Quaternion.gen_rotation_quaternion(
        _np.deg2rad(alpha), _np.deg2rad(beta), _np.deg2rad(gamma)
    )

    pos_vec = Quaternion._prepare_vector(self.x, self.y, self.z)
    x_rot, y_rot, z_rot = q_fwd * pos_vec
    return Point_Array3(
        x=x_rot.reshape(self.x.shape),
        y=y_rot.reshape(self.x.shape),
        z=z_rot.reshape(self.x.shape),
    )

Global Constants PI, PI/2, PI/4, µ0, and three internally used constants: FP_CUTOFF = 1e-8, ALIGN_CUTOFF = 1e-5, MAG_TOL = 1e-4