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 |
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),
)