Skip to content

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

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

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