Field visualisation using k3d#

Note: If you experience any problems in plotting with k3d, please make sure you run the Jupyter notebook in Google Chrome.

There are several ways how a field can be visualised, using:

  • matplotlib -k3d

  • holoviews

  • vtk-based libraries, e.g. pyvista

k3d provides three-dimensional interactive plots of fields inside Jupyter notebook.

Let us say we have a sample, which is an ellipsoid

\[\frac{x^2}{a^2} + \frac{y^2}{b^2} + \frac{z^2}{c^2} <= 1\]

with \(a=5\,\text{nm}\), \(b=3\,\text{nm}\), and \(c=2\,\text{nm}\). The space is discretised into cells with dimensions \((0.5\,\text{nm}, 0.5\,\text{nm}, 0.5\,\text{nm})\). The value of the field at \((x, y, z)\) point is \((-cy, cx, cz)\), with \(c=10^{9}\). The norm of the field inside the cylinder is \(10^{6}\).

Let us first build that field.

[1]:
import discretisedfield as df

a, b, c = 5e-9, 3e-9, 2e-9
cell = (0.5e-9, 0.5e-9, 0.5e-9)

mesh = df.Mesh(p1=(-a, -b, -c), p2=(a, b, c), cell=cell)


def norm_fun(pos):
    x, y, z = pos
    if (x / a) ** 2 + (y / b) ** 2 + (z / c) ** 2 <= 1:
        return 1e6
    else:
        return 0


def value_fun(pos):
    x, y, z = pos
    c = 1e9
    return (-c * y, c * x, c * z)


field = df.Field(mesh, nvdim=3, value=value_fun, norm=norm_fun)

The most basic plot we can show is the plot of all the cells where the value is non-zero. This can be useful, to inspect the domain created, by plotting the norm.

[2]:
field.norm.k3d.nonzero()

The plot is interactive, so it can be manipulated using a mouse. To change the color of voxels, we can pass the new color via color argument.

[3]:
field.norm.k3d.nonzero(color=0x27AE60)

Next, we can plot a scalar field. For plotting a scalar field, we are using discretisedfield.Field.k3d.scalar() method.

[4]:
field.k3d.scalar()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 1
----> 1 field.k3d.scalar()

File ~/repos/ubermag-devtools/repos/discretisedfield/discretisedfield/plotting/k3d_field.py:248, in K3dField.scalar(self, plot, filter_field, cmap, multiplier, interactive_field, **kwargs)
    246 if self.data.nvdim != 1:
    247     msg = f"Cannot plot nvdim={self.data.nvdim} field."
--> 248     raise ValueError(msg)
    250 if plot is None:
    251     plot = k3d.plot()

ValueError: Cannot plot nvdim=3 field.

An exception was raised because we attempted to plot a vector field using voxels. Therefore, we first need to extract a component of the field. Let us plot the \(x\) component.

[5]:
field.x.k3d.scalar()

However, we can see that the points which we consider to be outside the sample are also plotted. This is because, discretisedfield.Field.k3d.scalar() method cannot determine the points where norm is zero from the passed scalar field. Therefore, we need to pass field.norm as the filter_field.

[6]:
field.x.k3d.scalar(filter_field=field.norm, multiplier=1e-6)

By cascading operations, we can similarly plot the slice of the ellipsoid at \(z=0\).

[7]:
field.sel(x=(0, 0)).orientation.z.k3d.scalar()

To further modify the plot, keyword arguments for k3d.factory.voxels() function are accepted. Please refer to its documentation

Next, we can plot the vector field itself.

[8]:
field.k3d.vector()

By default, points at the discretisation cell centres are plotted together with vectors to help understand the structure of the field. However, they can be deactivated by passing points=False.

[9]:
field.k3d.vector(points=False)

It is difficult to understand the vector field from this plot. By cascading, we can plot its slice at \(x=0\).

[10]:
field.orientation.sel(x=(0, 0)).k3d.vector()

To improve the understanding of the plot, we can now colour the vectors plotted. For that, we need to pass a scalar field, according to which the vectors will be coloured.

[11]:
field.orientation.sel(x=(0, 0)).k3d.vector(color_field=field.x)

To further modify the plot, keyword arguments for k3d.factory.vectors() function are accepted. Plese refer to its documentation.

Multiple visualisation on the same plot#

Sometimes, it is necessary to show, for example, multiple planes of the sample on the same plot. This can be done by exposing the k3d.plot and passing it to different plotting methods. For instance.

[12]:
import k3d

plot = k3d.plot()
field.sel(x=(-3e-9, -3e-9)).k3d.vector(plot=plot, color_field=field.z)
field.sel(x=(0, 0)).k3d.vector(plot=plot, color_field=field.z, cmap="hsv")
field.sel(x=(3e-9, 3e-9)).k3d.vector(plot=plot, color_field=field.z)
plot.display()

k3d interactive plots#

[13]:
field.sel(z=(0, 0)).z.k3d.scalar(filter_field=field.norm)
[14]:
import k3d

plot1 = k3d.plot()


@df.interact(z=field.mesh.slider("z", continuous_update=True))
def myplot(z):
    field.z.sel(z=(z, z)).k3d.scalar(
        filter_field=field.norm, interactive_field=field, plot=plot1
    )


plot1.display()
[15]:
plot = k3d.plot()


@df.interact(
    z=field.mesh.slider("z", continuous_update=True), axis=field.mesh.axis_selector()
)
def myplot(z, axis):
    getattr(field, axis).sel(z=(z, z)).k3d.scalar(
        filter_field=field.norm, interactive_field=field, plot=plot
    )


plot.display()
[16]:
plot = k3d.plot()


@df.interact(
    y=field.mesh.slider("y", continuous_update=True), axis=field.mesh.axis_selector()
)
def myplot(y, axis):
    field.sel(y=(y, y)).k3d.vector(
        color_field=getattr(field, axis), interactive_field=field, plot=plot
    )


plot.display()