Plots module
pymagnets.plots
This module imports the classes and functions in the private modules to create a public API.
- Lines and contour plots are drawn using matplotlib
- 3D surface and volume plots are rendered using plotly.
Plotting routines for calculating along symmetry lines of cubes, cuboids, and cylinders
plot_1D_field(magnet, unit='mm', **kwargs)
Calculates and plots the magnetic field along the central symmetry axis of a cylinder or cuboid magnet, assuming the magnetic field is collinear
Parameters:
Name | Type | Description | Default |
---|---|---|---|
magnet |
Magnet3D |
Must be a Magnet3D type of magnet, either Prism, Cube,or Cylinder. |
required |
Kwargs
num_points (int): Number of points to calculate. Defaults to 101.
Returns:
Type | Description |
---|---|
tuple |
Point_Array1, Field1: point array struct containing z and the unit (e/g. 'mm'), vector array containing Bz and the field unit (e.g. 'T'). |
Source code in pymagnet/plots/_plot1D.py
def plot_1D_field(magnet, unit="mm", **kwargs):
"""Calculates and plots the magnetic field along the central symmetry axis
of a cylinder or cuboid magnet, assuming the magnetic field is collinear
Args:
magnet (Magnet3D): Must be a Magnet3D type of magnet, either Prism, Cube,or Cylinder.
Kwargs:
num_points (int): Number of points to calculate. Defaults to 101.
Returns:
tuple: Point_Array1, Field1: point array struct containing z and the unit (e/g. 'mm'), vector array containing Bz and the field unit (e.g. 'T').
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
num_points = kwargs.pop("num_points", 101)
return_data = kwargs.pop("return_data", False)
points = Point_Array1(_np.zeros(num_points), unit=unit)
if issubclass(magnet.__class__, Cylinder):
mag_boundary = magnet.length / 2
points.z = _np.linspace(
-2 * magnet.length + magnet.center[2],
2 * magnet.length + magnet.center[2],
num_points,
)
field = magnetic_field_cylinder_1D(magnet, points.z)
# if true, apply NaNs to inside the magnet
if magnet._mask_magnet:
mask = _generate_mask_1D(mag_boundary, magnet.center[2], points.z)
field.z[mask] = _np.NaN
elif issubclass(magnet.__class__, Prism):
mag_boundary = magnet.height / 2
points.z = _np.linspace(
-2 * magnet.height + magnet.center[2],
2 * magnet.height + magnet.center[2],
num_points,
)
field = magnetic_field_prism_1D(magnet, points.z)
# if true, apply NaNs to inside the magnet
if magnet._mask_magnet:
mask = _generate_mask_1D(mag_boundary, magnet.center[2], points.z)
field.z[mask] = _np.NaN
else:
print("Error")
return None
fig, ax = _plt.subplots(figsize=(8, 8))
unit_length = "(" + points.unit + ")"
field_unit = "(" + field.unit + ")"
_plt.xlabel(r"$z$ " + unit_length)
_plt.ylabel(r"$B_z$ " + field_unit)
_plt.plot(points.z, field.z)
_plt.axvline(x=-mag_boundary + magnet.center[2], c="blue", ls="--")
_plt.axvline(x=mag_boundary + magnet.center[2], c="red", ls="--")
_plt.axvline(x=0.0, c="k", ls="-")
_plt.show()
if return_data:
return points, field
Plotting routines
This module contains all functions needed to plot lines and contours for 2D magnetic sources, and
arrow
Encodes magnetisation vector for drawing on plots
Source code in pymagnet/plots/_plot2D.py
class arrow(object):
"""Encodes magnetisation vector for drawing on plots"""
def __init__(self, x, y, dx, dy, transform, width=3):
"""Init Arrow
Args:
x (float): arrow tail, x
y (float): arrow tail, y
dx (float): arrow head displacement, x
dy (float): arrow head displacement, y
transform (matplotlib Affine2D): transformation object, `translate`
width (int, optional): Arrow width. Defaults to 3.
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
super().__init__()
self.x = x
self.y = y
self.dx = dx
self.dy = dy
self.transform = transform
self.width = width
def __repr__(self) -> str:
return (
f"(x: {self.x}, y: {self.y}, dx: {self.dx}, dy: {self.dy}, w:{self.width})"
)
def __str__(self) -> str:
return (
f"(x: {self.x}, y: {self.y}, dx: {self.dx}, dy: {self.dy}, w:{self.width})"
)
__init__(self, x, y, dx, dy, transform, width=3)
special
Init Arrow
Parameters:
Name | Type | Description | Default |
---|---|---|---|
x |
float |
arrow tail, x |
required |
y |
float |
arrow tail, y |
required |
dx |
float |
arrow head displacement, x |
required |
dy |
float |
arrow head displacement, y |
required |
transform |
matplotlib Affine2D |
transformation object, |
required |
width |
int |
Arrow width. Defaults to 3. |
3 |
Source code in pymagnet/plots/_plot2D.py
def __init__(self, x, y, dx, dy, transform, width=3):
"""Init Arrow
Args:
x (float): arrow tail, x
y (float): arrow tail, y
dx (float): arrow head displacement, x
dy (float): arrow head displacement, y
transform (matplotlib Affine2D): transformation object, `translate`
width (int, optional): Arrow width. Defaults to 3.
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
super().__init__()
self.x = x
self.y = y
self.dx = dx
self.dy = dy
self.transform = transform
self.width = width
magnet_patch
Magnet drawing class
Source code in pymagnet/plots/_plot2D.py
class magnet_patch(object):
"""Magnet drawing class"""
def __init__(self, patch, arrow) -> None:
super().__init__()
self.patch = patch
self.arrow = arrow
def __str__(self) -> str:
return self.patch.__str__() + self.arrow.__str__()
patch
Encodes magnet dimensions for drawing on plots
Source code in pymagnet/plots/_plot2D.py
class patch(object):
"""Encodes magnet dimensions for drawing on plots"""
def __init__(self, x, y, width, height, transform, type):
"""Initialse a patch
Args:
x (float): centre, x
y (float): center, y
width (float): width
height (float): width
transform (matplotlib Affine2D): transform object, `rotate_deg_around`
"""
super().__init__()
self.x = x
self.y = y
self.width = width
self.height = height
self.transform = transform
self.type = type
def __repr__(self) -> str:
return f"(x: {self.x}, y: {self.y} w:{self.width}, h: {self.height})"
def __str__(self) -> str:
return f"(x: {self.x}, y: {self.y} w:{self.width}, h: {self.height})"
__init__(self, x, y, width, height, transform, type)
special
Initialse a patch
Parameters:
Name | Type | Description | Default |
---|---|---|---|
x |
float |
centre, x |
required |
y |
float |
center, y |
required |
width |
float |
width |
required |
height |
float |
width |
required |
transform |
matplotlib Affine2D |
transform object, |
required |
Source code in pymagnet/plots/_plot2D.py
def __init__(self, x, y, width, height, transform, type):
"""Initialse a patch
Args:
x (float): centre, x
y (float): center, y
width (float): width
height (float): width
transform (matplotlib Affine2D): transform object, `rotate_deg_around`
"""
super().__init__()
self.x = x
self.y = y
self.width = width
self.height = height
self.transform = transform
self.type = type
contour_plot_cylinder(magnet, **kwargs)
Calculates and plots the magnetic field of a cylinder
This is an example helper function.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
magnet |
Cylinder |
instance of magnetic cylinder |
required |
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def contour_plot_cylinder(magnet, **kwargs):
"""Calculates and plots the magnetic field
of a cylinder
This is an example helper function.
Args:
magnet (Cylinder): instance of magnetic cylinder
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
NP = 101
NPJ = NP * 1j
rho, z = _np.mgrid[
-3 * magnet.radius : 3 * magnet.radius : NPJ,
-magnet.length : magnet.length : NPJ,
]
Br, Bz = magnet._calcB_cyl(rho, z)
Bn = _np.sqrt(Bz ** 2 + Br ** 2)
xlab = "r (m)"
ylab = "z (m)"
# plot_B = Bn
clab = r"$|B|$ (T)"
cmap = "viridis"
fig, ax = plot_sub_contour_3D(
rho * 1,
z * 1,
Bn,
xlab=xlab,
ylab=ylab,
clab=clab,
cmap=cmap,
cmin=0,
cmax=1.0,
)
return fig, ax
line_plot_cylinder(magnet, **kwargs)
Calculates and plots the magnetic field along the central axis of a cylinder
This is an example helper function.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
magnet |
Cylinder |
instance of magnetic cylinder |
required |
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def line_plot_cylinder(magnet, **kwargs):
"""Calculates and plots the magnetic field along the central axis
of a cylinder
This is an example helper function.
Args:
magnet (Cylinder): instance of magnetic cylinder
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
rho = _np.linspace(-2 * magnet.radius, 2 * magnet.radius, 51)
z = _np.array([magnet.length * 1.1 / 2])
Br, Bz = magnet._calcB_cyl(rho, z)
fig, ax = _plt.subplots(figsize=(8, 8))
_plt.plot(rho * 1, Bz, label=r"$B_z$")
_plt.plot(rho * 1, Br, label=r"$B_r$")
_plt.legend(loc="best")
_plt.show()
return fig, ax
plot_2D_contour(point_array, field, **kwargs)
Contour plot of field
Parameters:
Name | Type | Description | Default |
---|---|---|---|
point_array |
Point_Array2 |
coordinates |
required |
field |
Field2 |
Magnetic Field |
required |
Kwargs
save_fig (bool): Save to png file. Defaults to False
xlab (str): x axis label
ylab (str): y axis label
clab (str): label for colorbar
axis_scale (str): axis aspect ratio Defaults to 'equal'.
show_magnets (bool): Draw magnets. Defaults to True
field_component (str): Defaults to 'n'.
plot_type (str): Draw contour
or streamplot
. Defaults to 'contour'
cmap (str): Colormap. Defaults to viridis
num_arrows (int or None): Number of arrows per axis. Defaults to None.
vector_color (str): Arrow color. Defaults to 'w'
cmin (float): Color scale minimum. Defaults to 0.0
cmax (float): Color scale minimum. Defaults to twice the mean field
num_levels (int): Number of contour levels. Defaults to 11.
Exceptions:
Type | Description |
---|---|
Exception |
plot_type must be 'contour' or 'streamplot' |
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def plot_2D_contour(point_array, field, **kwargs):
"""Contour plot of field
Args:
point_array (Point_Array2): coordinates
field (Field2): Magnetic Field
Kwargs:
save_fig (bool): Save to png file. Defaults to False
xlab (str): x axis label
ylab (str): y axis label
clab (str): label for colorbar
axis_scale (str): axis aspect ratio Defaults to 'equal'.
show_magnets (bool): Draw magnets. Defaults to True
field_component (str): Defaults to 'n'.
plot_type (str): Draw `contour` or `streamplot`. Defaults to 'contour'
cmap (str): Colormap. Defaults to `viridis`
num_arrows (int or None): Number of arrows per axis. Defaults to None.
vector_color (str): Arrow color. Defaults to 'w'
cmin (float): Color scale minimum. Defaults to 0.0
cmax (float): Color scale minimum. Defaults to twice the mean field
num_levels (int): Number of contour levels. Defaults to 11.
Raises:
Exception: plot_type must be 'contour' or 'streamplot'
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
from ..magnets._polygon2D import PolyMagnet
show_magnets = kwargs.pop("show_magnets", True)
xlab = kwargs.pop("xlab", f"x ({point_array.unit})")
ylab = kwargs.pop("ylab", f"y ({point_array.unit})")
clab = kwargs.pop("clab", f"B ({field.unit})")
axis_scale = kwargs.pop("axis_scale", "equal")
SAVE = kwargs.pop("save_fig", False)
field_component = kwargs.pop("field_component", "n")
plot_type = kwargs.pop("plot_type", "contour")
fig, ax = _plt.subplots(figsize=(8, 8))
if plot_type.lower() == "contour":
cmap = kwargs.pop("cmap", "viridis")
vector_color = kwargs.pop("vector_color", "w")
NQ = kwargs.pop("num_arrows", None)
if field_component == "x":
field_chosen = field.x
elif field_component == "y":
field_chosen = field.y
else:
field_chosen = field.n
finite_field = field_chosen[_np.isfinite(field_chosen)]
cmin = kwargs.pop("cmin", 0)
cmax = kwargs.pop("cmax", round(finite_field.mean() * 2, 1))
num_levels = kwargs.pop("num_levels", 11)
lev2 = _np.linspace(cmin, cmax, 256, endpoint=True)
CS = _plt.contourf(
point_array.x,
point_array.y,
field_chosen,
levels=lev2,
cmap=_plt.get_cmap(cmap),
extend="max",
)
# Draw contour lines
if num_levels > 1:
lev1 = _np.linspace(cmin, cmax, num_levels, endpoint=True)
_ = _plt.contour(
point_array.x,
point_array.y,
field_chosen,
vmin=cmin,
vmax=cmax,
levels=lev1,
linewidths=1.0,
colors="k",
)
CB = _plt.colorbar(CS, ticks=lev1)
else:
CB = _plt.colorbar(CS)
# Draw field vectors
if NQ is not None:
_vector_plot2(point_array, field, NQ, vector_color)
elif plot_type.lower() == "streamplot":
xpl = point_array.x[:, 0]
ypl = point_array.y[0, :]
cmap = kwargs.pop("cmap", None)
if cmap is not None:
cmin = kwargs.pop("cmin", -round(_np.nanmean(field.n), 1))
cmax = kwargs.pop("cmax", round(_np.nanmean(field.n), 1))
stream_shading = kwargs.pop("stream_color", "vertical")
norm = _cm.colors.Normalize(vmin=cmin, vmax=cmax)
stream_dict = {
"normal": field.n.T,
"horizontal": field.x.T,
"vertical": field.y.T,
}
CS = _plt.streamplot(
xpl,
ypl,
field.x.T / field.n.T,
field.y.T / field.n.T,
color=stream_dict.get(stream_shading, "normal"),
density=1.2,
norm=norm,
cmap=cmap,
linewidth=0.5,
)
CB = _plt.colorbar(CS.lines)
else:
color = kwargs.pop("color", "k")
CS = _plt.streamplot(
xpl,
ypl,
field.x.T / field.n.T,
field.y.T / field.n.T,
density=1.2,
linewidth=0.5,
color=color,
)
CB = None
else:
raise Exception("plot_type must be 'contour' or 'streamplot'")
# Draw magnets and magnetisation arrows
if show_magnets:
_draw_magnets2(ax)
if len(PolyMagnet.instances) > 0:
for magnet in PolyMagnet.instances:
poly = _plt.Polygon(
_np.array(magnet.polygon.vertices),
ec="k",
fc="w",
zorder=5,
)
ax.add_patch(poly)
if CB is not None:
CB.ax.get_yaxis().labelpad = 15
CB.ax.set_ylabel(clab, rotation=270)
_plt.axis(axis_scale)
_plt.xlabel(xlab)
_plt.ylabel(ylab)
_plt.show()
fig.tight_layout()
ax.axis("scaled")
if SAVE:
_plt.savefig("contour_plot.png", dpi=300)
return fig, ax
plot_2D_line(point_array, field, **kwargs)
Line Plot of field from 2D magnet
Parameters:
Name | Type | Description | Default |
---|---|---|---|
point_array |
Point_Array2 |
coordinates |
required |
field |
Field2 |
Magnetic Field |
required |
Kwargs
xlab (str): xlabel ylab (str): ylabel axis_scale (str): unused save_fig (bool): Save to png file. Defaults to False
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def plot_2D_line(point_array, field, **kwargs):
"""Line Plot of field from 2D magnet
Args:
point_array (Point_Array2): coordinates
field (Field2): Magnetic Field
Kwargs:
xlab (str): xlabel
ylab (str): ylabel
axis_scale (str): unused
save_fig (bool): Save to png file. Defaults to False
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
xlab = kwargs.pop("xlab", f"x ({point_array.unit})")
ylab = kwargs.pop("ylab", f"B ({field.unit})")
# axis_scale = kwargs.pop("axis_scale", "equal")
SAVE = kwargs.pop("save_fig", False)
fig, ax = _plt.subplots(figsize=(8, 8))
_plt.plot(point_array.x, field.n, label=r"$|\mathbf{B}|$")
_plt.plot(point_array.x, field.x, label=r"$B_x$")
_plt.plot(point_array.x, field.y, label=r"$B_y$")
_plt.legend(loc="best")
_plt.xlabel(xlab)
_plt.ylabel(ylab)
_plt.show()
if SAVE:
_plt.savefig("line_plot.png", dpi=300)
# _plt.savefig('contour_plot.pdf', dpi=300)
return fig, ax
plot_3D_contour(points, field, plane, **kwargs)
Contour plot of field
Parameters:
Name | Type | Description | Default |
---|---|---|---|
points |
Point_Array2 |
coordinates |
required |
field |
Field2 |
Magnetic field |
required |
plane |
str |
Plane to draw contour on. Can be 'xy', 'xz', or 'yz' |
required |
Exceptions:
Type | Description |
---|---|
Exception |
plot_type must be 'contour' or 'streamplot |
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def plot_3D_contour(points, field, plane, **kwargs):
"""Contour plot of field
Args:
points (Point_Array2): coordinates
field (Field2): Magnetic field
plane (str): Plane to draw contour on. Can be 'xy', 'xz', or 'yz'
Raises:
Exception: plot_type must be 'contour' or 'streamplot
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
axis_scale = kwargs.pop("axis_scale", "equal")
plot_type = kwargs.pop("plot_type", "contour")
xlab = kwargs.pop("xlab", "x (" + points.unit + ")")
ylab = kwargs.pop("ylab", "y (" + points.unit + ")")
zlab = kwargs.pop("zlab", "z (" + points.unit + ")")
clab = kwargs.pop("clab", "B (" + field.unit + ")")
SAVE = kwargs.pop("save_fig", False)
finite_field = field.n[_np.isfinite(field.n)]
cmax = kwargs.pop("cmax", round(finite_field.mean() * 2, 1))
num_levels = kwargs.pop("num_levels", 11)
if plane.lower() == "xy":
plot_x = points.x
plot_y = points.y
plot_xlab = xlab
plot_ylab = ylab
stream_x = field.x
stream_y = field.z
elif plane.lower() == "xz":
stream_x = field.x
stream_y = field.z
plot_x = points.x
plot_y = points.z
plot_xlab = xlab
plot_ylab = zlab
else:
stream_x = field.y
stream_y = field.z
plot_x = points.y
plot_y = points.z
plot_xlab = ylab
plot_ylab = zlab
fig, ax = _plt.subplots(figsize=(8, 8))
# Generate Contour Plot
if plot_type.lower() == "contour":
vector_color = kwargs.pop("vector_color", "w")
NQ = kwargs.pop("num_arrows", None)
cmap = kwargs.pop("cmap", "viridis")
cmin = kwargs.pop("cmin", 0)
lev2 = _np.linspace(cmin, cmax, 256, endpoint=True)
CS = _plt.contourf(
plot_x,
plot_y,
field.n,
levels=lev2,
cmap=_plt.get_cmap(cmap),
extend="max",
)
# Draw contour lines
if num_levels > 1:
lev1 = _np.linspace(cmin, cmax, num_levels, endpoint=True)
_ = _plt.contour(
plot_x,
plot_y,
field.n,
vmin=cmin,
vmax=cmax,
levels=lev1,
linewidths=1.0,
colors="k",
)
CB = _plt.colorbar(CS, ticks=lev1)
else:
CB = _plt.colorbar(CS)
if NQ is not None:
B_2D = Field2(stream_x, stream_y, unit=field.unit)
B_2D.n = field.n
points_2D = Point_Array2(plot_x, plot_y, unit=points.unit)
_vector_plot2(points_2D, B_2D, NQ, vector_color)
# Generates streamplot
elif plot_type.lower() == "streamplot":
xpl = plot_x[:, 0]
ypl = plot_y[0, :]
cmap = kwargs.pop("cmap", None)
if cmap is not None:
cmin = kwargs.pop("cmin", -round(finite_field.mean() * 2, 1))
cmax = kwargs.pop("cmax", round(finite_field.mean() * 2, 1))
stream_shading = kwargs.pop("stream_shading", "vertical")
norm = _cm.colors.Normalize(vmin=cmin, vmax=cmax)
stream_dict = {
"normal": field.n.T,
"horizontal": stream_x.T,
"vertical": stream_y.T,
}
CS = _plt.streamplot(
xpl,
ypl,
stream_x.T / field.n.T,
stream_y.T / field.n.T,
color=stream_dict.get(stream_shading, "normal"),
density=1.2,
norm=norm,
cmap=cmap,
linewidth=0.5,
)
CB = _plt.colorbar(CS.lines)
else:
color = kwargs.pop("color", "k")
CS = _plt.streamplot(
xpl,
ypl,
stream_x.T / field.n.T,
stream_y.T / field.n.T,
density=1.2,
linewidth=0.5,
color=color,
)
CB = None
else:
raise Exception("plot_type must be 'contour' or 'streamplot'")
if CB is not None:
CB.ax.get_yaxis().labelpad = 15
CB.ax.set_ylabel(clab, rotation=270)
_plt.xlabel(plot_xlab)
_plt.ylabel(plot_ylab)
_plt.axis(axis_scale)
if SAVE:
_plt.savefig("contour_plot.png", dpi=300)
return fig, ax
plot_sub_contour_3D(plot_x, plot_y, plot_B, **kwargs)
Contour plot of a single magnetic field component of a 3D simulation
Parameters:
Name | Type | Description | Default |
---|---|---|---|
plot_x |
ndarray |
coordinates for x-axis of plot |
required |
plot_y |
ndarray |
coordinates for y-axis of plot |
required |
plot_B |
ndarray |
Magnetic field component to plot |
required |
Returns:
Type | Description |
---|---|
tuple |
fig, ax reference to matplotlib figure and axis objects |
Source code in pymagnet/plots/_plot2D.py
def plot_sub_contour_3D(plot_x, plot_y, plot_B, **kwargs):
"""Contour plot of a single magnetic field component of a 3D simulation
Args:
plot_x (ndarray): coordinates for x-axis of plot
plot_y (ndarray): coordinates for y-axis of plot
plot_B (ndarray): Magnetic field component to plot
Returns:
tuple: fig, ax reference to matplotlib figure and axis objects
"""
if not _has_matplotlib:
raise ImportError("matplotlib is required to use this plot function.")
cmap = kwargs.pop("cmap", "seismic")
xlab = kwargs.pop("xlab", "x (m)")
ylab = kwargs.pop("ylab", "y (m)")
clab = kwargs.pop("clab", "B (T)")
# axis_scale = kwargs.pop("axis_scale", "equal")
# SAVE = kwargs.pop("save_fig", False)
cmin = kwargs.pop("cmin", -0.5)
cmax = kwargs.pop("cmax", 0.5)
num_levels = kwargs.pop("num_levels", 11)
lev2 = _np.linspace(cmin, cmax, 256, endpoint=True)
fig, ax = _plt.subplots(figsize=(8, 8))
CS = _plt.contourf(
plot_x, plot_y, plot_B, levels=lev2, cmap=_plt.get_cmap(cmap), extend="both"
)
if num_levels > 1:
lev1 = _np.linspace(cmin, cmax, num_levels, endpoint=True)
_ = _plt.contour(
plot_x,
plot_y,
plot_B,
vmin=cmin,
vmax=cmax,
levels=lev1,
linewidths=1.0,
colors="k",
)
CB = _plt.colorbar(CS, ticks=lev1)
else:
CB = _plt.colorbar(CS)
CB.ax.get_yaxis().labelpad = 15
CB.ax.set_ylabel(clab, rotation=270)
_plt.xlabel(xlab)
_plt.ylabel(ylab)
_plt.axis("equal")
_plt.show()
return fig, ax
3D Plotting routines
This module contains all functions needed to plot 3D contours for 3D magnetic sources. Unlike the plot2D module, here plotly is used as the backend.
Todo
- Update str and repr for polyhedra
Graphic_Cuboid (Polyhedron)
Generates Cuboid for plotly rendering
Source code in pymagnet/plots/_plotly3D.py
class Graphic_Cuboid(Polyhedron):
"""Generates Cuboid for plotly rendering"""
def __init__(self, center=(0, 0, 0), size=(1, 1, 1), **kwargs):
"""Init method
Args:
center (tuple, optional): Cuboid center. Defaults to (0, 0, 0).
size (tuple, optional): Size of cuboid. Defaults to (1, 1, 1).
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size, **kwargs)
self.vertices = self.generate_vertices()
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
) > Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(center=(0, 0, 0), size=self.size)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.size)
return vertex_coords
@staticmethod
def _gen_vertices(center=(0, 0, 0), size=(1, 1, 1)):
"""Generates coordinates for all cuboid vertices
Args:
center (tuple, optional): x,y,z coordinates. Defaults to (0, 0, 0)
size (tuple, optional): scale the cuboid in x, y, z directons. Defaults to (1, 1, 1).
Returns:
ndarray: numpy array of shape (3, 8)
"""
# Center of this cube is (0.5, 0.5, 0.5)
x = _np.array([0, 0, 1, 1, 0, 0, 1, 1]) - 0.5
y = _np.array([0, 1, 1, 0, 0, 1, 1, 0]) - 0.5
z = _np.array([0, 0, 0, 0, 1, 1, 1, 1]) - 0.5
vertex_coords = _np.vstack([x, y, z])
# Scale x, y, z by the size array
vertex_coords = _np.multiply(vertex_coords.T, size).T
# Offset by externally set center
vertex_coords += _np.array(center).reshape(-1, 1)
return vertex_coords
__init__(self, center=(0, 0, 0), size=(1, 1, 1), **kwargs)
special
Init method
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
tuple |
Cuboid center. Defaults to (0, 0, 0). |
(0, 0, 0) |
size |
tuple |
Size of cuboid. Defaults to (1, 1, 1). |
(1, 1, 1) |
Kwargs
alpha (float): rotation wit respect to ? axis. Defaults to 0.0. beta (float): rotation wit respect to ? axis. Defaults to 0.0. gamma (float): rotation wit respect to ? axis. Defaults to 0.0. color (str): color. Defaults to 'w'
Source code in pymagnet/plots/_plotly3D.py
def __init__(self, center=(0, 0, 0), size=(1, 1, 1), **kwargs):
"""Init method
Args:
center (tuple, optional): Cuboid center. Defaults to (0, 0, 0).
size (tuple, optional): Size of cuboid. Defaults to (1, 1, 1).
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size, **kwargs)
self.vertices = self.generate_vertices()
generate_vertices(self)
Generates and rotates vertices of a cuboid based on orientation angles
Returns:
Type | Description |
---|---|
ndarray |
3xN array of vertex coordinates (columns are x, y, z) |
Source code in pymagnet/plots/_plotly3D.py
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
) > Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(center=(0, 0, 0), size=self.size)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.size)
return vertex_coords
Graphic_Cylinder (Polyhedron)
Generates vertices for a Cylinder for plotly rendering
Source code in pymagnet/plots/_plotly3D.py
class Graphic_Cylinder(Polyhedron):
"""Generates vertices for a Cylinder for plotly rendering"""
def __init__(self, center=(0, 0, 0), radius=1, length=1, **kwargs):
"""Init method
Args:
center (tuple, optional): [description]. Defaults to (0, 0, 0).
radius (float, optional): [description]. Defaults to 1.
length (float, optional): [description]. Defaults to 1.
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size=(radius, radius, length), **kwargs)
self.radius = radius
self.length = length
self.vertices = self.generate_vertices()
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
)
> Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(
center=(0, 0, 0), radius=self.radius, length=self.length
)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.radius, self.length)
return vertex_coords
@staticmethod
def _gen_vertices(center=(0, 0, 0), radius=1, length=1):
"""Generates coordinates for approximate cylinder vertices
Args:
center (tuple, optional): x,y,z coordinates. Defaults to (0, 0, 0)
radius (float, optional): radius of the cylinder. Defaults to 1.
length (float, optional): length of the cylinder. Defaults to 1.
Returns:
ndarray: numpy array of shape (3, 8)
"""
rho, z = _np.mgrid[0 : 2 * PI : 40j, -length / 2 : length / 2 : 2j]
x = radius * _np.cos(rho)
y = radius * _np.sin(rho)
vertex_coords = _np.vstack([x.ravel(), y.ravel(), z.ravel()])
# Offset by externally set center
vertex_coords += _np.array(center).reshape(-1, 1)
return vertex_coords
__init__(self, center=(0, 0, 0), radius=1, length=1, **kwargs)
special
Init method
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
tuple |
[description]. Defaults to (0, 0, 0). |
(0, 0, 0) |
radius |
float |
[description]. Defaults to 1. |
1 |
length |
float |
[description]. Defaults to 1. |
1 |
Kwargs
alpha (float): rotation wit respect to ? axis. Defaults to 0.0. beta (float): rotation wit respect to ? axis. Defaults to 0.0. gamma (float): rotation wit respect to ? axis. Defaults to 0.0. color (str): color. Defaults to 'w'
Source code in pymagnet/plots/_plotly3D.py
def __init__(self, center=(0, 0, 0), radius=1, length=1, **kwargs):
"""Init method
Args:
center (tuple, optional): [description]. Defaults to (0, 0, 0).
radius (float, optional): [description]. Defaults to 1.
length (float, optional): [description]. Defaults to 1.
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size=(radius, radius, length), **kwargs)
self.radius = radius
self.length = length
self.vertices = self.generate_vertices()
generate_vertices(self)
Generates and rotates vertices of a cuboid based on orientation angles
Returns:
Type | Description |
---|---|
ndarray |
3xN array of vertex coordinates (columns are x, y, z) |
Source code in pymagnet/plots/_plotly3D.py
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
)
> Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(
center=(0, 0, 0), radius=self.radius, length=self.length
)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.radius, self.length)
return vertex_coords
Graphic_Mesh (Polyhedron)
Generates Mesh from STL file for plotly rendering
Source code in pymagnet/plots/_plotly3D.py
class Graphic_Mesh(Polyhedron):
"""Generates Mesh from STL file for plotly rendering"""
def __init__(self, mesh_vectors, **kwargs):
"""Init Method
Args:
mesh_vectors (ndarray): array of mesh vectors
Kwargs:
color (str): magnet color. Defaults to 'w'.
"""
self.color = kwargs.pop("color", "white")
self.mesh_vectors = mesh_vectors
def generate_vertices(self):
"""Generates vertices from STL file
Returns:
dict: Dictionary of rendering properties for plotly
"""
# return super().generate_vertices()
p, q, r = self.mesh_vectors.shape # (p, 3, 3)
# the array stl_mesh.vectors.reshape(p*q, r) can contain multiple copies of the same vertex;
# extract unique vertices from all mesh triangles
vertices, ixr = _np.unique(
self.mesh_vectors.reshape(p * q, r), return_inverse=True, axis=0
)
_I = _np.take(ixr, [3 * k for k in range(p)])
_J = _np.take(ixr, [3 * k + 1 for k in range(p)])
_K = _np.take(ixr, [3 * k + 2 for k in range(p)])
x, y, z = vertices.T
trace = _go.Mesh3d(x=x, y=y, z=z, i=_I, j=_J, k=_K, color=self.color)
# optional parameters to make it look nicer
trace.update(
flatshading=True, lighting_facenormalsepsilon=0, lighting_ambient=0.7
)
return trace
__init__(self, mesh_vectors, **kwargs)
special
Init Method
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mesh_vectors |
ndarray |
array of mesh vectors |
required |
Kwargs
color (str): magnet color. Defaults to 'w'.
Source code in pymagnet/plots/_plotly3D.py
def __init__(self, mesh_vectors, **kwargs):
"""Init Method
Args:
mesh_vectors (ndarray): array of mesh vectors
Kwargs:
color (str): magnet color. Defaults to 'w'.
"""
self.color = kwargs.pop("color", "white")
self.mesh_vectors = mesh_vectors
generate_vertices(self)
Generates vertices from STL file
Returns:
Type | Description |
---|---|
dict |
Dictionary of rendering properties for plotly |
Source code in pymagnet/plots/_plotly3D.py
def generate_vertices(self):
"""Generates vertices from STL file
Returns:
dict: Dictionary of rendering properties for plotly
"""
# return super().generate_vertices()
p, q, r = self.mesh_vectors.shape # (p, 3, 3)
# the array stl_mesh.vectors.reshape(p*q, r) can contain multiple copies of the same vertex;
# extract unique vertices from all mesh triangles
vertices, ixr = _np.unique(
self.mesh_vectors.reshape(p * q, r), return_inverse=True, axis=0
)
_I = _np.take(ixr, [3 * k for k in range(p)])
_J = _np.take(ixr, [3 * k + 1 for k in range(p)])
_K = _np.take(ixr, [3 * k + 2 for k in range(p)])
x, y, z = vertices.T
trace = _go.Mesh3d(x=x, y=y, z=z, i=_I, j=_J, k=_K, color=self.color)
# optional parameters to make it look nicer
trace.update(
flatshading=True, lighting_facenormalsepsilon=0, lighting_ambient=0.7
)
return trace
Graphic_Sphere (Polyhedron)
Generates vertices for a sphere for plotly rendering
Source code in pymagnet/plots/_plotly3D.py
class Graphic_Sphere(Polyhedron):
"""Generates vertices for a sphere for plotly rendering"""
def __init__(self, center=(0, 0, 0), radius=1, **kwargs):
"""Init method
Args:
center (tuple, optional): [description]. Defaults to (0, 0, 0).
radius (float, optional): [description]. Defaults to 1.
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size=(radius, radius, radius), **kwargs)
self.radius = radius
self.vertices = self.generate_vertices()
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
)
> Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(center=(0, 0, 0), radius=self.radius)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.radius)
return vertex_coords
@staticmethod
def _gen_vertices(center=(0, 0, 0), radius=1):
"""Generates coordinates for approximate sphere vertices
Args:
center (tuple, optional): x,y,z coordinates. Defaults to (0, 0, 0)
radius (float, optional): radius of the sphere. Defaults to 1.
Returns:
ndarray: numpy array of shape (3, 8)
"""
u, v = _np.mgrid[0 : 2 * PI : 20j, 0:PI:10j]
x = radius * _np.cos(u) * _np.sin(v)
y = radius * _np.sin(u) * _np.sin(v)
z = radius * _np.cos(v)
vertex_coords = _np.vstack([x.ravel(), y.ravel(), z.ravel()])
# Offset by externally set center
vertex_coords += _np.array(center).reshape(-1, 1)
return vertex_coords
__init__(self, center=(0, 0, 0), radius=1, **kwargs)
special
Init method
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
tuple |
[description]. Defaults to (0, 0, 0). |
(0, 0, 0) |
radius |
float |
[description]. Defaults to 1. |
1 |
Kwargs
alpha (float): rotation wit respect to ? axis. Defaults to 0.0. beta (float): rotation wit respect to ? axis. Defaults to 0.0. gamma (float): rotation wit respect to ? axis. Defaults to 0.0. color (str): color. Defaults to 'w'
Source code in pymagnet/plots/_plotly3D.py
def __init__(self, center=(0, 0, 0), radius=1, **kwargs):
"""Init method
Args:
center (tuple, optional): [description]. Defaults to (0, 0, 0).
radius (float, optional): [description]. Defaults to 1.
Kwargs:
alpha (float): rotation wit respect to ? axis. Defaults to 0.0.
beta (float): rotation wit respect to ? axis. Defaults to 0.0.
gamma (float): rotation wit respect to ? axis. Defaults to 0.0.
color (str): color. Defaults to 'w'
"""
super().__init__(center, size=(radius, radius, radius), **kwargs)
self.radius = radius
self.vertices = self.generate_vertices()
generate_vertices(self)
Generates and rotates vertices of a cuboid based on orientation angles
Returns:
Type | Description |
---|---|
ndarray |
3xN array of vertex coordinates (columns are x, y, z) |
Source code in pymagnet/plots/_plotly3D.py
def generate_vertices(self):
"""Generates and rotates vertices of a cuboid based on orientation angles
Returns:
ndarray: 3xN array of vertex coordinates (columns are x, y, z)
"""
# Generate and rotate the vertices
if _np.any(
_np.fabs(
_np.array(
[
self.alpha_rad,
self.beta_rad,
self.gamma_rad,
]
)
)
> Polyhedron.tol
):
# _, reverse_rotation = self._generate_rotation_quaternions()
forward_rotation = Quaternion.gen_rotation_quaternion(
self.alpha_rad, self.beta_rad, self.gamma_rad
)
reverse_rotation = forward_rotation.get_conjugate()
# Generate 3xN array for quaternion rotation
vertex_coords = self._gen_vertices(center=(0, 0, 0), radius=self.radius)
# Rotate points
x, y, z = reverse_rotation * vertex_coords
# Reconstruct 3xN array and add center offset
vertex_coords = _np.vstack([x, y, z])
vertex_coords += _np.array(self.center).reshape(-1, 1)
# finally return the coordinates
return vertex_coords
# Only generate
else:
vertex_coords = self._gen_vertices(self.center, self.radius)
return vertex_coords
Polyhedron (Registry)
Encodes magnet dimensions for drawing a polyhedon on 3D plots
Polyhedra
Cuboid Cylinder Sphere Mesh
Source code in pymagnet/plots/_plotly3D.py
class Polyhedron(Registry):
"""Encodes magnet dimensions for drawing a polyhedon on 3D plots
Polyhedra:
Cuboid
Cylinder
Sphere
Mesh
"""
# Tolerance for minimum angle needed for rotation of object
tol = MAG_TOL
def __init__(self, center, size, **kwargs):
"""Initialises a cuboid
Args:
center (tuple): x,y,z
size (tuple): x,y,z
"""
super().__init__()
self.center = _np.asarray(center)
self.size = _np.asarray(size)
self.alpha = kwargs.pop("alpha", 0.0)
self.alpha_rad = _np.deg2rad(self.alpha)
self.beta = kwargs.pop("beta", 0.0)
self.beta_rad = _np.deg2rad(self.beta)
self.gamma = kwargs.pop("gamma", 0.0)
self.gamma_rad = _np.deg2rad(self.gamma)
self.color = kwargs.pop("color", "white")
def __repr__(self) -> str:
return f"(center: {self.center}, size: {self.size} )"
def __str__(self) -> str:
return f"(center: {self.center}, size: {self.size} )"
def generate_vertices(self):
"""Generates vertices of a polyhedron
This should be implemented for each Polyhedron child class
"""
pass
__init__(self, center, size, **kwargs)
special
Initialises a cuboid
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
tuple |
x,y,z |
required |
size |
tuple |
x,y,z |
required |
Source code in pymagnet/plots/_plotly3D.py
def __init__(self, center, size, **kwargs):
"""Initialises a cuboid
Args:
center (tuple): x,y,z
size (tuple): x,y,z
"""
super().__init__()
self.center = _np.asarray(center)
self.size = _np.asarray(size)
self.alpha = kwargs.pop("alpha", 0.0)
self.alpha_rad = _np.deg2rad(self.alpha)
self.beta = kwargs.pop("beta", 0.0)
self.beta_rad = _np.deg2rad(self.beta)
self.gamma = kwargs.pop("gamma", 0.0)
self.gamma_rad = _np.deg2rad(self.gamma)
self.color = kwargs.pop("color", "white")
generate_vertices(self)
Generates vertices of a polyhedron
This should be implemented for each Polyhedron child class
Source code in pymagnet/plots/_plotly3D.py
def generate_vertices(self):
"""Generates vertices of a polyhedron
This should be implemented for each Polyhedron child class
"""
pass
list_polyhedra()
Returns a list of all instantiated polyhedra.
Assumes that the child class registries have not been modified outside of
using pymagnet.reset_magnets()
.
Source code in pymagnet/plots/_plotly3D.py
def list_polyhedra():
"""Returns a list of all instantiated polyhedra.
Assumes that the child class registries have not been modified outside of
using `pymagnet.reset_magnets()`.
"""
return Polyhedron.print_instances()
plot_magnet(unit='mm', **kwargs)
Renders magnets
Parameters:
Name | Type | Description | Default |
---|---|---|---|
unit |
str |
unit scale. Defaults to 'mm'. |
'mm' |
Returns:
Type | Description |
---|---|
fig |
reference to figure |
Source code in pymagnet/plots/_plotly3D.py
def plot_magnet(unit="mm", **kwargs):
"""Renders magnets
Args:
unit (str, optional): unit scale. Defaults to 'mm'.
Returns:
fig: reference to figure
"""
if not _has_plotly:
raise ImportError("plotly is required to use this plot function.")
reset_polyhedra()
magnet_opacity = kwargs.pop("magnet_opacity", 1.0)
data_objects = []
data_objects.extend(_generate_all_meshes(magnet_opacity=magnet_opacity))
fig = _go.Figure(data=data_objects)
fig.update_layout(
scene=dict(
xaxis_title="x (" + unit + ")",
yaxis_title="y (" + unit + ")",
zaxis_title="z (" + unit + ")",
),
width=700,
margin=dict(r=20, b=10, l=10, t=10),
)
fig.update_layout(scene_aspectmode="data")
fig.show()
return fig
reset_polyhedra()
Returns a list of all instantiated polyhedra.
Source code in pymagnet/plots/_plotly3D.py
def reset_polyhedra():
"""Returns a list of all instantiated polyhedra."""
polyhedra_classes = [
Polyhedron,
Graphic_Cuboid,
Graphic_Sphere,
Graphic_Cylinder,
]
for cls in polyhedra_classes:
cls.reset()
slice_plot(data_dict, **kwargs)
Plots magnetic field slices. A convenience function.
Returns:
Type | Description |
---|---|
tuple |
fig (reference to figure), data_objects (plotly dict) |
Source code in pymagnet/plots/_plotly3D.py
def slice_plot(data_dict, **kwargs):
"""Plots magnetic field slices.
A convenience function.
Returns:
tuple: fig (reference to figure), data_objects (plotly dict)
"""
if not _has_plotly:
raise ImportError("plotly is required to use this plot function.")
reset_polyhedra()
opacity = kwargs.pop("opacity", 0.8)
magnet_opacity = kwargs.pop("magnet_opacity", 1.0)
cone_opacity = kwargs.pop("cone_opacity", 1.0)
cmin = kwargs.pop("cmin", 0)
cmax = kwargs.pop("cmax", 0.5)
colorscale = kwargs.pop("colorscale", "viridis")
num_arrows = kwargs.pop("num_arrows", None)
data_objects = []
show_magnets = kwargs.pop("show_magnets", True)
if show_magnets:
data_objects.extend(_generate_all_meshes(magnet_opacity=magnet_opacity))
for plane in data_dict.keys():
points = data_dict[plane]["points"]
field = data_dict[plane]["field"]
data_objects.append(
_draw_surface_slice(
points,
field,
colorscale,
opacity=opacity,
cmin=cmin,
cmax=cmax,
showscale=True,
)
)
if num_arrows is not None:
num_points = field.x.shape[0]
NA = num_points // num_arrows
if NA > 1:
data_objects.append(
_draw_cones(points, field, NA=NA, cone_opacity=cone_opacity)
)
fig = _go.Figure(data=data_objects)
fig.update_layout(
scene=dict(
xaxis_title="x (" + points.unit + ")",
yaxis_title="y (" + points.unit + ")",
zaxis_title="z (" + points.unit + ")",
),
width=700,
margin=dict(r=20, b=10, l=10, t=10),
)
fig.update_layout(scene_aspectmode="data")
fig.show()
return fig, data_objects
slice_quickplot(**kwargs)
Calculates and plots magnetic field slices. A convenience function.
Returns:
Type | Description |
---|---|
tuple |
fig (reference to figure), cache (cached data for each plane with potential keys: 'xy', 'xz', 'yz'), data_objects (plotly dict) |
Source code in pymagnet/plots/_plotly3D.py
def slice_quickplot(**kwargs):
"""Calculates and plots magnetic field slices.
A convenience function.
Returns:
tuple: fig (reference to figure), cache (cached data for each plane with potential keys: 'xy', 'xz', 'yz'), data_objects (plotly dict)
"""
if not _has_plotly:
raise ImportError("plotly is required to use this plot function.")
reset_polyhedra()
max1 = kwargs.pop("max1", 30)
max2 = kwargs.pop("max2", 30)
min1 = kwargs.pop("min1", -1 * max1)
min2 = kwargs.pop("min2", -1 * max2)
slice_value = kwargs.pop("slice_value", 0.0)
unit = kwargs.pop("unit", "mm")
opacity = kwargs.pop("opacity", 0.8)
magnet_opacity = kwargs.pop("magnet_opacity", 1.0)
cone_opacity = kwargs.pop("cone_opacity", 1.0)
planes = kwargs.pop("planes", ["xy", "xz", "yz"])
num_arrows = kwargs.pop("num_arrows", None)
num_points = kwargs.pop("num_points", 100)
if num_arrows is not None:
NA = num_points // num_arrows
if NA < 1:
NA = 1
cmin = kwargs.pop("cmin", 0)
cmax = kwargs.pop("cmax", 0.5)
colorscale = kwargs.pop("colorscale", "viridis")
data_objects = []
cache = {}
show_magnets = kwargs.pop("show_magnets", True)
if show_magnets:
data_objects.extend(_generate_all_meshes(magnet_opacity=magnet_opacity))
for plane in planes:
points = slice3D(
plane=plane,
max1=max1,
min1=min1,
max2=max2,
min2=min2,
slice_value=slice_value,
unit=unit,
num_points=num_points,
)
field = get_field_3D(points)
cache[plane] = {"points": points, "field": field}
data_objects.append(
_draw_surface_slice(
points,
field,
colorscale,
opacity=opacity,
cmin=cmin,
cmax=cmax,
showscale=True,
)
)
if num_arrows is not None:
data_objects.append(
_draw_cones(points, field, NA=NA, cone_opacity=cone_opacity)
)
fig = _go.Figure(data=data_objects)
fig.update_layout(
scene=dict(
xaxis_title="x (" + points.unit + ")",
yaxis_title="y (" + points.unit + ")",
zaxis_title="z (" + points.unit + ")",
),
width=700,
margin=dict(r=20, b=10, l=10, t=10),
)
fig.update_layout(scene_aspectmode="data")
fig.show()
return fig, cache, data_objects
volume_plot(points, field, **kwargs)
Plots magnetic field volume.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
points |
Point_Array3 |
coordinates |
required |
field |
Field3 |
Magnetic field vector |
required |
Returns:
Type | Description |
---|---|
tuple |
fig (reference to figure), data_objects (plotly dict) |
Source code in pymagnet/plots/_plotly3D.py
def volume_plot(points, field, **kwargs):
"""Plots magnetic field volume.
Args:
points (Point_Array3): coordinates
field (Field3): Magnetic field vector
Returns:
tuple: fig (reference to figure), data_objects (plotly dict)
"""
if not _has_plotly:
raise ImportError("plotly is required to use this plot function.")
reset_polyhedra()
opacity = kwargs.pop("opacity", 0.3)
opacityscale = kwargs.pop("opacityscale", None)
magnet_opacity = kwargs.pop("magnet_opacity", 1.0)
cone_opacity = kwargs.pop("cone_opacity", 1.0)
num_arrows = kwargs.pop("num_arrows", None)
cmin = kwargs.pop("cmin", 0)
cmax = kwargs.pop("cmax", 0.5)
num_levels = kwargs.pop("num_levels", 5)
show_magnets = kwargs.pop("show_magnets", True)
num_points = len(points.x)
data_objects = []
if show_magnets:
data_objects.extend(_generate_all_meshes(magnet_opacity=magnet_opacity))
colorscale = kwargs.pop("colorscale", "viridis")
# kernel_size = 1
# kernel = np.ones([kernel_size, kernel_size, kernel_size]) / kernel_size
# B.n = ndimage.convolve(B.n, kernel)
data_objects.append(
_generate_volume_data(
points,
field,
cmim=cmin,
cmax=cmax,
opacity=opacity,
colorscale=colorscale,
num_levels=num_levels,
opacityscale=opacityscale,
)
)
if num_arrows is not None:
NA = num_points // num_arrows
if NA < 1:
NA = 1
data_objects.append(
_draw_cones(points, field, NA=NA, cone_opacity=cone_opacity)
)
fig = _go.Figure(data=data_objects)
fig.update_layout(
scene=dict(
xaxis_title="x (" + points.unit + ")",
yaxis_title="y (" + points.unit + ")",
zaxis_title="z (" + points.unit + ")",
),
width=700,
margin=dict(r=20, b=10, l=10, t=10),
)
fig.update_layout(scene_aspectmode="data")
fig.show()
return fig, data_objects
volume_quickplot(**kwargs)
Calculates and plots magnetic field slices. A convenience function.
Kwargs
num_points (int): = kwargs.pop("num_points", 30) unit (str): = kwargs.pop("unit", "mm") xmax (float): Maximum x value. Defaults to 30.0. ymax (float): Maximum y value. Defaults to 30.0. zmax (float): Maximum z value. Defaults to 30.0. xmin (float): Minimum x value. Defaults to -xmax ymin (float): Minimum y value. Defaults to -ymax zmin (float): Minimum z value. Defaults to -zmax
Returns:
Type | Description |
---|---|
tuple |
fig (reference to figure), cache (cached data dict), data_objects (plotly dict) |
Source code in pymagnet/plots/_plotly3D.py
def volume_quickplot(**kwargs):
"""Calculates and plots magnetic field slices.
A convenience function.
Kwargs:
num_points (int): = kwargs.pop("num_points", 30)
unit (str): = kwargs.pop("unit", "mm")
xmax (float): Maximum x value. Defaults to 30.0.
ymax (float): Maximum y value. Defaults to 30.0.
zmax (float): Maximum z value. Defaults to 30.0.
xmin (float): Minimum x value. Defaults to -xmax
ymin (float): Minimum y value. Defaults to -ymax
zmin (float): Minimum z value. Defaults to -zmax
Returns:
tuple: fig (reference to figure), cache (cached data dict), data_objects (plotly dict)
"""
if not _has_plotly:
raise ImportError("plotly is required to use this plot function.")
num_points = kwargs.pop("num_points", 30)
unit = kwargs.pop("unit", "mm")
xmax = kwargs.pop("xmax", 30)
ymax = kwargs.pop("ymax", 30)
zmax = kwargs.pop("zmax", 30)
xmin = kwargs.pop("xmin", -1 * xmax)
ymin = kwargs.pop("ymin", -1 * ymax)
zmin = kwargs.pop("zmin", -1 * zmax)
points = grid3D(
xmax,
ymax,
zmax,
num_points=num_points,
xmin=xmin,
ymin=ymin,
zmin=zmin,
unit=unit,
)
field = get_field_3D(points)
fig, data_objects = volume_plot(points, field, num_points=num_points, **kwargs)
cache = {"points": points, "field": field}
return fig, cache, data_objects