This tool numerically solves for the 3D vector magnetic field around an arbitrarily shaped coil specified by the user, in a discrete and finite volume surrounding the coil. The code is optimized to use NumPy vectorization for fast calculations. Latest Version: V5.0 (June 20, 2021)
By Mingde Yin and Ryan Zazo
Given an input coil, the tool can both calculate, and plot the magnetic field vector over a finite discrete volume of space around the coil.
The coil is passed into the program as a set of vertices describing the geometry of the coil, as well as the amount of current flowing through a given segment of the coil. (See Formatting section)
A target volume is produced as a series of points within a rectangular prism bounding box around the coil. The magnetic field at each point in the target volume is obtained by numerically integrating using the Biot-Savart law, resulting in a 4D array of size (X, Y, Z, 3) as the output, which is saved to a file using numpy.save()
.
The entire process of producing the finished target volume is done through the function write_target_volume()
.
A calculated target volume can be loaded in using read_target_volume()
. This target volume array can then be plotted using the built-in plotting functions.
- Have Python 3.x installed
- Install
numpy
andmatplotlib
- Clone or download the repository to your machine
You can produce and calculate a target volume using write_target_volume()
, which writes a calcualted target volume to a file which can be loaded and used later using read_target_volume(filename)
.
Parameters Breakdown
input_filename
: Name of input file. See Coil Format for detailsoutput_filename
: The name of the file in which we store the finished target volume.box_size
: (x, y, z) tuple with the dimensions of the target volume in cmstart_point
: (x, y, z) tuple representing the bottom left corner spatial position of the target volume (with respect to the coil) AKA the offsetcoil_resolution
: How long each discretized coil subsegment should be, in cmvolume_resolution
: Division of volumetric meshgrid (generate a point in the target volume every volume_resolution cm)
Increasing coil_resolution
affects runtime linearly, whereas increasing volume_resolution
increases runtime cubically.
coil_resolution
can be reduced to around 0.01 cm with acceptable results, and volume_resolution
should be kept no lower than 0.1 cm. We recommend using 1 cm for both, if you can tolerate it.
The coil is represented as a series of (X,Y,Z) coordinates which define the vertices of the coil spatially, along with an additional coordinate (I) which defines the amount of current flowing from that vertex to the next one.
- Plaintext file
- The file contains each (X,Y,Z,I) entry on a new line
- The format of each line should be
"x,y,z,I\n"
For instance:
0,0,1,2
0,1,1,3
1,1,1,4
Means that:
- There are 2 amps of current running between points 1 and 2
- There are 3 amps of current running between points 2 and 3
- The last bit of current is functionally useless.
An Example Coil (coil.txt
):
0,0,0,1
10,0,0,1
10,10,0,1
20,10,0,1
Here's what the coil looks like (static/coil.svg
), plotted using plot_coil("coil.txt")
.
All positions in this program are described with respect to the position of the coil. This means, for the above coil, a box_size
of (10, 10, 10)
, with a start_point
of (-5, -5, -5)
creates a box with its middle centred about the first vertex of the coil at (0, 0, 0)
. In other words, it's as you would expect.
When indexing a finished target volume, you must be wary of both the volume_resolution
and the start_point
. For instance, a volume_resolution
of 0.5 cm means that would would need to first subtract the starting point, and then multiply incoming coordinates by 2 to get the correct array index for the target volume.
If you are happy with just reading the field vector at some point in space, there is the built in function get_field_vector()
, which will do this for you. However, you you want to perform complex operations on the system, it is advisable to work with the full size arrays directly.
plot_coil()
takes in an input filename for a coil stored in the format above, and graphs is in 3D.
plot_fields()
produces contour plots of the Bx, By, and Bz components of the magnetic field along a certain plane.
Parameters Breakdown
Bfields
: a calculated target volume array, which can be read fromread_target_volume()
start_point, box_size, vol_resolution
: Same as abovewhich_plane
: ['x', 'y', 'z'] the plane upon which you wish to graphlevel
: the level of the plane on which you wish to graph (same coordinate system as the coil)num_contours
: the number of contours produced
Example: Helmholtz Coils, plotted at z = 0
Results in a graph that looks like:
If you want to plot the effects of multiple coils, you can simply save several target volumes with the same box_size
, start_point
, and vol_resolution
, then add them together (See Helmholtz Coil Example Below).
This approach is ideal for systems with complicated geometry, as it is easy to construct a several simple coils, and analyse their overall effect by adding their fields together in this way.
You can either produce your own coils according to the rules specified above, or use some of the built in functions.
create_B_x_rectangle()
, create_B_y_rectangle()
, create_B_x_rectangle()
produce rectangular coils of adjustable dimensions at adjustable positions.
Similarly, create_Bx_circle()
, create_By_circle()
, create_By_circle()
produce circular coils of adjustable dimensions at adjustable positions.
helmholtz_coils()
creates a pair of circular helmholtz coils, spaced apart in the z axis.
coil.txt
:
0,0,0,1
10,0,0,1
10,10,0,1
20,10,0,1
import biot_savart as bs
bs.write_target_volume("coil.txt", "coil", (30, 15, 15), (-5, -0.5, -2.5), 1, 1)
# generates a target volume from the coil stored at coil.txt
# uses a 30 x 15 x 15 bounding box, starting at (-5, -0.5, -2.5)
# uses 1 cm resolution
bs.plot_coil("coil.txt")
# plots the coil stored at coil.txt
fields, positions = bs.read_target_volume("coil")
# reads the volume we created
bs.plot_fields(fields, positions, which_plane='z', level=5, num_contours=50)
# plots the fields we just produced
# plotting along the plane x = 5, with 50 contours
Output:
import biot_savart as bs
bs.helmholtz_coils("helm1.txt", "helm2.txt", 50, 5, 2, 1)
# makes a pair of helmholtz coils
# 50 segments each, with radius of 5 cm
# spaced out by 2 cm, located at z = +/- 1 respectively
# 1 amp of current
bs.plot_coil("helm1.txt", "helm2.txt")
bs.write_target_volume("helm1.txt", "targetvol1", (10, 10, 10), (-5, -5, -5), 0.5, 0.5)
bs.write_target_volume("helm2.txt", "targetvol2", (10, 10, 10), (-5, -5, -5), 0.5, 0.5)
# use a target volume of size 10, centred about origin
h1, pos1 = bs.read_target_volume("targetvol1")
h2, pos2 = bs.read_target_volume("targetvol2")
# produce the target volumes we want
# use linear superposition of magnetic fields, to get the combined effects of multiple coils
h_total = h1 + h2
bs.plot_fields(h_total, pos1, which_plane='z', level=0, num_contours=50)
The tool uses a couple of techniques to accelerate computation and increase precision.
- Evaluation of the target volume is performed using numpy meshgrids, which improve speed dramatically; almost 1000 times faster than using traditional for loops.
- Integration is performed using Richardson Extrapolation, which allows for higher precision without needing extra function evaluations.
-
v1: Initial Release
-
v2: Code accelerated using numpy meshgrids
-
v2.1: Tkinter dialogs for opening & saving files. Defaults of 1 cm resolution in calculation.
-
v3: Plotting code integrated.
-
v3.1: Minor cosmetic improvements to plot.
-
v3.2: 3D plot of coil geometry.
-
v3.3/3.4: Plotted B-fields together but code is long.
-
v3.5/3.6: all B-field plots together
-
v3.7: B-fields plotted together with 50 levels (now works on windows) and combined v3.3 and v3.5
-
v3.8: Changed up all np.aranges to np.linspaces and changed up the plotting code to work with non-integer step sizes and non-integer levels
-
v4: Using Richardson Extrapolation for midpoint rule to improve accuracy (5 to 30x better at 1.4x speed penalty), tweaked linspaces to correctly do step size
-
v4.1: Minor change in function indexing to use more numpy, cleaning up for export
-
v4.2: Changed the linspaces a bit to make everything more symmetric
-
v4.3: Added in many functions for plotting rectangular and circular coils, cleaned up functions to be more consistent, and flexible.
-
v5.0: Rewrite of some major parts of code, make the core computation engine fully vectorized