""" Helmoltz coils ============== A script that computes the magnetic field generated by a pair of Helmoltz coils. """ import numpy as np from scipy import special, linalg ############################################################################## # Function to caculate the field of a loop def base_vectors(n): """ Returns 3 orthognal base vectors, the first one colinear to n. """ # normalize n n = n / np.sqrt(np.square(n).sum(axis=-1)) # choose two vectors perpendicular to n # choice is arbitrary since the coil is symetric about n if abs(n[0]) == 1 : l = np.r_[n[2], 0, -n[0]] else: l = np.r_[0, n[2], -n[1]] l = l / np.sqrt(np.square(l).sum(axis=-1)) m = np.cross(n, l) return n, l, m def B_field(r, n, r0, R): """ returns the magnetic field from an arbitrary current loop calculated from eqns (1) and (2) in Phys Rev A Vol. 35, N 4, pp. 1535-1546; 1987. Parameters ---------- n is normal vector to the plane of the loop at the center, current is oriented by the right-hand-rule. r is a position vector where the Bfield is evaluated: [x1 y2 z3 ; x2 y2 z2 ; ... ] r is in units of d r0 is the location of the center of the loop in units of d: [x y z] R is the radius of the loop Returns ------- B is a vector for the B field at point r in inverse units of (mu I) / (2 pi d) for I in amps and d in meters and mu = 4 pi * 10^-7 we get Tesla """ ### Translate the coordinates in the coil's frame n, l, m = base_vectors(n) # transformation matrix coil frame to lab frame trans = np.vstack((l, m, n)) # transformation matrix to lab frame to coil frame inv_trans = linalg.inv(trans) r = r - r0 #point location from center of coil r = np.dot(r, inv_trans) #transform vector to coil frame #### calculate field # express the coordinates in polar form x = r[:, 0] y = r[:, 1] z = r[:, 2] rho = np.sqrt(x**2 + y**2) theta = np.arctan(x / y) # NaNs are generated where y is zero. theta[y == 0] = np.pi / 2 E = special.ellipe((4 * R * rho)/( (R + rho)**2 + z**2)) K = special.ellipk((4 * R * rho)/( (R + rho)**2 + z**2)) dist = ((R - rho)**2 + z**2) Bz = 1 / np.sqrt((R + rho)**2 + z**2) * ( K + E * (R**2 - rho**2 - z**2) / dist ) Brho = z / (rho*np.sqrt((R + rho)**2 + z**2)) * ( -K + E * (R**2 + rho**2 + z**2)/ dist ) # On the axis of the coil we get a divided by zero here. This returns a # NaN, where the field is actually zero : Brho[dist == 0] = 0 Brho[rho == 0] = 0 Bz[dist == 0] = 0 B = np.c_[np.cos(theta)*Brho, np.sin(theta)*Brho, Bz ] # Rotate the field back in the lab's frame B = np.dot(B, trans) return B ############################################################################## # The grid of points on which we want to evaluate the field X, Y, Z = np.mgrid[-0.15:0.15:31j, -0.15:0.15:31j, -0.15:0.15:31j] # Avoid rounding issues : f = 1e4 # this gives the precision we are interested in: X = np.round(X * f) / f Y = np.round(Y * f) / f Z = np.round(Z * f) / f # The (x, y, z) position vector r = np.c_[np.ravel(X), np.ravel(Y), np.ravel(Z)] ############################################################################## # The coil positions # The center of the coil r0 = np.r_[0, 0, 0.1] # The normal to the coils n = np.r_[0, 0, 1] # The radius R = 0.1 # Add the mirror image of this coils relatively to the xy plane : r0 = np.vstack((r0, -r0 )) R = np.r_[R, R] n = np.vstack((n, n)) # Helmoltz like configuration ############################################################################## # Calculate field # First initialize a container matrix for the field vector : B = np.zeros_like(r) # Then loop through the different coils and sum the fields : for this_n, this_r0, this_R in zip(n, r0, R): this_n = np.array(this_n) this_r0 = np.array(this_r0) this_R = np.array(this_R) B += B_field(r, this_n, this_r0, this_R)