# Plotting

The `ComFiT`

package supports both the plotting library `plotly`

(default) and `matplotlib`

.

## Plotly

At the base of a plotly plot is a `figure`

object.
The plotting functions return a `figure`

object, which can be used to plot the field.
Multple `figure`

objects can be organized into a subplot using the `tool_make_subplots`

function.

## Matplotlib structure

The standard plotting library of comfit is `plotly`

.
However, the library was originally built on `matplotlib`

, and the plotting functions are still available by calling

which will return a `figure`

and `axes`

object that can be used to plot the field.
The basic structure of `matplotlib`

is that it has a `figure`

object that contains `axes`

objects.
One can think of the `figure`

object as the window in which the plot is drawn, and the `axes`

object as the plot itself.
A new figure is made as follows

Given a figure object, one may define a new `axes`

object on it as follows

`111`

is a shorthand for `1,1,1`

and means that the
If you are plotting a 3D object, then you will need to specify that
which will construct a 3D `axes`

object.

The convention followed in `ComFiT`

are as follows:

- When a plotting function is called
*without*a keyword argument specifying the current figure or axes, then the current figure will be cleared and potential axes (in the case of matplotlib) will be created onto it. This is because with no reference to which axes the plot is meant to go ontop, there is no way of knowing. - If a figure is provided by the keyword
`fig=myfig`

with, then it will be cleared and the new plot will be plotted on`myfig`

. This is because with no reference to which axes the plot is meant to go ontop, there is no way of knowing. - If an axes object is provided by the keyword
`ax`

, then the`ax`

instance will be cleared and the new plot will be plotted on`ax`

, unless the keyword`hold=True`

is provided, in which case the new plot will be plotted ontop of the old plot.

To show the current plot, one writes

which will pause the simulation until the plot window has been closed. In order to draw the image and continue the simulation, as for instance when viewing a simulation live, one needs to write

## Plotting keywords

The following list gives the keyword arguments that determine the layout of the resulting plot.
These keywords can be passed to any plot function.
`bs`

refers to an instance of the `BaseSystem`

class.
In some cases, default values of other parameter depend on the value of `dim`

, and are represented by curly brackets:

Keyword | Definition | Default value |
---|---|---|

`xlabel` |
The label on the x-axis | \(x/a_0\) |

`ylabel` |
The label on the y-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ y/a_0 \\ y/a_0 \\ \end{array} \right \rbrace\) |

`zlabel` |
The label on the z-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ \texttt{none} \\ z/a_0 \\ \end{array} \right \rbrace\) |

`suptitle` |
The figure title | None |

`title` |
The axes title | None |

`xmin` |
The lower limit on the x-axis | `bs.xmin` |

`xmax` |
The upper limit on the x-axis | `bs.xmax - bs.dx` |

`xlim` |
A list or tuple consisting of the lower and upper limit on the x-axis. If `xlim` is provided, it trumps any provided `xmin` or `xmax` . |
None |

`ymin` |
The lower limit on the y-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ \texttt{bs.ymin} \\ \texttt{bs.ymin} \\ \end{array} \right \rbrace\) |

`ymax` |
The upper limit on the y-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ \texttt{bs.ymax-bs.dy} \\ \texttt{bs.ymax-bs.dy} \\ \end{array} \right \rbrace\) |

`ylim` |
A list or tuple consisting of the lower and upper limit on the y-axis. If `ylim` is provided, it trumps any provided `ymin` or `ymax` . |
None |

`zmin` |
The lower limit on the z-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ \texttt{none} \\ \texttt{bs.zmin} \\ \end{array} \right \rbrace\) |

`zmax` |
The upper limit on the z-axis | \(\left \lbrace \begin{array}{c} \texttt{none} \\ \texttt{none} \\ \texttt{bs.zmax-bs.dz} \\ \end{array} \right \rbrace\) |

`zlim` |
List or tuple consisting of the lower and upper limit on the z-axis. If `zlim` is provided, it trumps any provided `zmin` or `zmax` . |
None |

`vmin` |
Lower limit on the field to be plotted. In the case of a complex function, this is the lower limit of the absolute value of the field to be plotted. | None |

`vmax` |
Upper limit on the value of field to be plotted. In the case of a complex function, this is the upper limit of the absolute value of the field to be plotted. | None |

`vlim` |
List or tuple consisting of the lower and upper limit of the value to be plotted. Only relevant for `plot_field` . |
None |

`vlim_symmetric` |
A Boolean parameter specifying whether the value limits should be symmetric. Only relevant for `plot_field` . |
`False` |

`colorbar` |
Boolean parameter indicating whether or not to plot the colorbar | `True` (if applicable) |

`colormap` |
String specifying the colormap to be used | Varies |

`grid` |
Boolean parameter indicating whether or not to plot the axes grid | `False` |

`hold` |
Boolean parameter indicating whether or not to hold the current plot | `False` |

`plot_shadows` |
Boolean parameter indicating whether or not to plot the shadows of the objects. Only applicable for `plot_complex_field` . |
`True` |

`fig` |
`matplotlib` figure handle |
None |

`ax` |
`matplotlib` axis handle |
None |

`xticks` |
List of ticks on the x-axis | None |

`xticklabels` |
List of labels for the ticks on the x-axis | None |

`yticks` |
List of ticks on the y-axis | None |

`yticklabels` |
List of labels for the ticks on the y-axis | None |

`zticks` |
List of ticks on the z-axis | None |

`zticklabels` |
List of labels for the ticks on the z-axis | None |

`cticks` |
List of ticks on the colorbar | None |

`cticklabels` |
List of labels for the ticks on the colorbar | None |

`alpha` |
The alpha value of the plot | 0.5 |

## Plotting functions

The `BaseSystem`

class comes pre-packaged with a number of plotting functions to plot four different types of fields.

### Real fields

Real fields are fields that take real values, for example the temperature in a room.

`plot_field`

The `plot_field`

function is used to plot a real field.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(131)
bs = cf.BaseSystem(1,xRes=31)
field = bs.x**2
bs.plot_field(field,ax=ax1)
ax2 = fig.add_subplot(132)
bs = cf.BaseSystem(2,xRes=31,yRes=31)
field = bs.x**2 + bs.y**2
bs.plot_field(field,ax=ax2)
ax3 = fig.add_subplot(133, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
field = bs.x**2 + bs.y**2 + bs.z**2
bs.plot_field(field,ax=ax3)
plt.show()
```

`plot_field_in_plane`

The `plot_field_in_plane`

function is used to plot a real field in a plane.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
field = (bs.x**2 + bs.y**2 + bs.z**2)
bs.plot_field_in_plane(field, ax=ax1)
ax2 = fig.add_subplot(122, projection='3d')
bs.plot_field_in_plane(field, ax=ax2, normal_vector=[1,1,0],position=[10,10,10])
plt.show()
```

### Complex fields

Complex fields are fields that take complex values, for example the electric field in a light wave.

`plot_complex_field`

The `plot_complex_field`

function is used to plot a complex field.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(231)
bs = cf.BaseSystem(1,xRes=31)
field = bs.x**2*np.exp(1j*bs.x/3)
bs.plot_complex_field(field,ax=ax1)
ax2 = fig.add_subplot(232)
bs = cf.BaseSystem(2,xRes=31,yRes=31)
field = (bs.x**2 + bs.y**2)*np.exp(1j*bs.x/3)
bs.plot_complex_field(field,ax=ax2,plot_method='phase_angle')
ax3 = fig.add_subplot(233, projection='3d')
bs = cf.BaseSystem(2,xRes=31,yRes=31)
field = (bs.x**2 + bs.y**2)*np.exp(1j*bs.x/3)
bs.plot_complex_field(field,ax=ax3,plot_method='3Dsurface')
ax5 = fig.add_subplot(235, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
field = (bs.x**2 + bs.y**2 + bs.z**2)*np.exp(1j*bs.x/3)
bs.plot_complex_field(field,ax=ax5,plot_method='phase_angle')
ax6 = fig.add_subplot(236, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
field = (bs.x**2 + bs.y**2 + bs.z**2)*np.exp(1j*bs.x/3)
bs.plot_complex_field(field,ax=ax6,plot_method='phase_blob')
plt.show()
```

`plot_complex_field_in_plane`

The `plot_complex_field_in_plane`

function is used to plot a complex field in a plane.
The modulus of the complex field is shown as the alpha channel, where the minimum modulus value is transparent and the maximum modulus value is opaque.
The phase of the complex field is shown as the color of the field, where the color is determined by the angle color scheme.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
complex_field = (bs.x**2 + bs.y**2 + bs.z**2)*np.exp(1j*bs.y/3)
bs.plot_complex_field_in_plane(complex_field, ax=ax1)
ax2 = fig.add_subplot(122, projection='3d')
bs.plot_complex_field_in_plane(complex_field, ax=ax2, normal_vector=[0,0,1],position=[10,10,10])
plt.show()
```

### Angle fields

Angle fields are fields that take values in the interval \([-\pi,\pi]\), for example the phase of a complex field.

`plot_angle_field`

The `plot_angle_field`

function is used to plot an angle field.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(131)
bs = cf.BaseSystem(1,xRes=31)
angle_field = np.mod((bs.x)/5,2*np.pi)-np.pi
bs.plot_angle_field(angle_field,ax=ax1)
ax2 = fig.add_subplot(132)
bs = cf.BaseSystem(2,xRes=31,yRes=31)
angle_field = np.mod((bs.x + 2*bs.y)/5,2*np.pi)-np.pi
bs.plot_angle_field(angle_field,ax=ax2)
ax3 = fig.add_subplot(133, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
angle_field = np.mod((bs.x + 2*bs.y + 3*bs.z)/5,2*np.pi)-np.pi
bs.plot_angle_field(angle_field,ax=ax3)
plt.show()
```

`plot_angle_field_in_plane`

The `plot_angle_field_in_plane`

function is used to plot an angle field in a plane.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
bs = cf.BaseSystem(3,xRes=31,yRes=31,zRes=31)
angle_field = np.mod((bs.x + 2*bs.y + 3*bs.z)/5,2*np.pi)-np.pi
bs.plot_angle_field_in_plane(angle_field, ax=ax1)
ax2 = fig.add_subplot(122, projection='3d')
bs.plot_angle_field_in_plane(angle_field, ax=ax2, normal_vector=[0,0,1],position=[10,10,10])
plt.show()
```

### Vector fields

`plot_vector_field`

The `plot_vector_field`

function is used to plot a vector field \(\mathbf v = (v_x,v_y,v_z)\).
Vector fields are usually plotted blue.
Together with the typical keyword arguments, the `plot_vector_field`

function has the kewyword `spacing`

which determines the spacing between the arrows in the plot.

The behavior of this plot function is dependent on the interplay between the dimension of the system and the dimension \(n\) of the vector field.
In cases where `dim`

\(+ n > 3\), it is not possible to plot the vector field in a quantitatively accurate (QA) way.
In such cases, different scalings which results in not quantitatively accurate representations (not QA) are taken to visualize the vector field, as described in the table below, and the user is encouraged to plot the vector field components individually for quantitative analysis.
The scaling used is can be seen in the code of the `plot_vector_field`

function, and a custom scaling can be provided by the user by setting the `vx_scale`

, `vy_scale`

and `vz_scale`

keyword arguments.
These factors scale the normalized vector field (\(\frac{\mathbf v = \mathbf v }{|\mathbf v|}\)) components in the x-, y- and z-axes, respectively, as shown for \(n=3\) below.

```
# Normalizing
U = U / max_vector
V = V / max_vector
W = W / max_vector
# Scale factors
vx_scale = kwargs.get('vx_scale', 2*spacing*self.xmax/max_vector)
vy_scale = kwargs.get('vy_scale', 2*spacing*self.ymax/max_vector)
vz_scale = kwargs.get('vz_scale', spacing)
# Scaling
U = vx_scale*U
V = vy_scale*V
W = vz_scale*W
```

The following table summarizes the behavior of the `plot_vector_field`

function.

System dimension | \(n=1\) | \(n=2\) | \(n=3\) |
---|---|---|---|

`dim=1` |
\(v_x\) on y-axis (QA). |
\(v_x\) on y-axis, \(v_y\) on z-axis (QA). |
\(v_x, v_y\) and \(v_z\) along x-, y- and z-axes, respectively (not QA). |

`dim=2` |
\(v_x\) on the x-axis. (not QA) |
\(v_x\) and \(v_y\) on x- and y-axes, respectively (not QA). |
\(v_x\), \(v_y\) and \(v_z\) on the x-, y- and z-axes, respectively (not QA). |

`dim=3` |
\(v_x\) on the x-axis (not QA) |
\(v_x\), \(v_y\) on the x-, and y-xes, respectively (not QA). |
\(v_x\), \(v_y\) and \(v_z\) on the x-, y- and z-axes, respectively (not QA). |

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
#1D system
bs = cf.BaseSystem(1,xRes=31)
# 1D vector field
ax1 = fig.add_subplot(331)
vector_field = np.array([bs.x*np.cos(bs.x/5)])
bs.plot_vector_field(vector_field,ax=ax1, spacing=1)
# 2D vector field
ax2 = fig.add_subplot(332, projection='3d')
vector_field = np.array([bs.x*np.cos(bs.x/5), bs.x*np.sin(bs.x/5)])
bs.plot_vector_field(vector_field,ax=ax2, spacing=2)
# 3D vector field
ax3 = fig.add_subplot(333, projection='3d')
vector_field = np.array([bs.x*np.cos(bs.x/5), bs.x*np.sin(bs.x/5), bs.x*np.cos(bs.x/5)])
bs.plot_vector_field(vector_field,ax=ax3, spacing=3)
#2D system
bs = cf.BaseSystem(2,xRes=31,yRes=31)
# 1D vector field
ax4 = fig.add_subplot(334)
vector_field = np.array([bs.x*np.cos(bs.y/5)])
bs.plot_vector_field(vector_field,ax=ax4,spacing=3)
# 2D vector field
ax5 = fig.add_subplot(335)
vector_field = np.array([bs.x*np.cos(bs.y/5), bs.y*np.sin(bs.x/5)])
bs.plot_vector_field(vector_field,ax=ax5,spacing=5)
# 3D vector field
ax6 = fig.add_subplot(336, projection='3d')
vector_field = np.array([bs.x*np.cos(bs.y/5), bs.y*np.sin(bs.x/5), bs.x*np.cos(bs.y/5)])
bs.plot_vector_field(vector_field,ax=ax6, spacing=3)
# 3D system
bs = cf.BaseSystem(3,xRes=11,yRes=11,zRes=11)
# 1D vector field
ax7 = fig.add_subplot(337, projection='3d')
vector_field = np.array([bs.z+bs.x*np.cos(bs.y/5)])
bs.plot_vector_field(vector_field,ax=ax7,spacing=3)
# 2D vector field
ax8 = fig.add_subplot(338, projection='3d')
vector_field = np.array([bs.z+ bs.x*np.cos(bs.y/5), bs.z + bs.y*np.sin(bs.x/5)])
bs.plot_vector_field(vector_field,ax=ax8,spacing=5)
# 3D vector field
ax9 = fig.add_subplot(339, projection='3d')
vector_field = np.array([bs.z+ bs.x*np.cos(bs.y/5), bs.z + bs.y*np.sin(bs.x/5), -bs.z + bs.x*np.cos(bs.y/5)])
bs.plot_vector_field(vector_field,ax=ax9,spacing=3)
plt.show()
```

`plot_vector_field_in_plane`

The `plot_vector_field_in_plane`

function is used to plot a vector field in a plane.

## Example

```
import comfit as cf
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax1 = fig.add_subplot(121, projection='3d')
bs = cf.BaseSystem(3,xRes=11,yRes=11,zRes=11)
vector_field = np.array([bs.z+bs.x*np.cos(bs.y/5), bs.z+bs.y*np.sin(bs.x/5), -bs.z+bs.x*np.cos(bs.y/5)])
bs.plot_vector_field_in_plane(vector_field, ax=ax1)
ax2 = fig.add_subplot(122, projection='3d')
bs = cf.BaseSystem(3,xRes=11,yRes=11,zRes=11)
vector_field = np.array([bs.z+bs.x*np.cos(bs.y/5), bs.z+bs.y*np.sin(bs.x/5)])
bs.plot_vector_field_in_plane(vector_field, ax=ax2, normal_vector=[0,1,1],position=[2,3,3])
plt.show()
```

## Animation

Creating animations are typically done by exporting each frame to a `png`

-file and then combining the frames together.
If `plot_lib`

is set to `matplotlib`

(default), then the command to export a frame is given by

where `n`

is the frame number (assumed to start at `0`

).
If `plot_lib`

is set to `plotly`

, then the command to export a frame is given by

where `n`

is the frame number (assumed to start at `0`

) and `fig`

is the `plotly`

figure.

In both cases, an optional argument `ID`

can be given, which assigns a unique identifier to the plot, which is useful in case of running multiple simulations in parallel.

After producing the individual figures, they can be combined into an animation using the command

where `n`

is the last frame number.
This command is the same for both `matplotlib`

and `plotly`

.

## Example

Here is how one would create an animation of a field.

For `self.plot_lib = 'matplotlib'`

:

```
# Initialize the field
for n in range(100):
# Update the field
# Plot the field
cf.save_plot(n)
cf.tool_make_animation_gif(n)
```

And for `self.plot_lib = 'plotly'`

:

## Angle color scheme

In many of the plotting functions, we are plotting angles, for example in plotting the phase of a complex number or the value of an order parameter on S1 . In these cases, all values modulus 2π are eqvuivalent, but if one uses a regular color scheme, this equivalence is not readily visible. Therefore, when expressing angles, we use the color scheme shown in Fig. 1.1. This has the benefit of wrapping around itself at θ = ±π, stressing that these correspond

*Angle color scheme.* The color scheme follows the hsv color circle going through \(\theta=0\) (Red), \(\theta=\pi/3\) (Yellow), \(\theta=2\pi/3\) (Lime), \(\theta = \pm \pi\) (Aqua), \(\theta = -2\pi/3\) (Blue), \(\theta = -\pi/3\) (Fuchsia).

### Technicality: The `marching_cubes`

function and interpolation

The marching cubes algorithm is used to create a 3D surface from a 3D field and is used in creating many of the plots in three dimensions. If you are going to make changes to the codebase, then it is useful to have an idea of how it works and what the resulting quantities are.

Typically, we have our 3D system with a total resolution of, say 300, and a field `field`

, of which we want to extract the values on some specific isosurface `iso_value`

.
The `marching_cubes`

function is called as follows

`verts`

is a list of the (integer) positions of the vertices of the surfaces, e.g.,

```
verts =
[[x0i,y0i,z0i],
[x1i,y1i,z1i],
[x2i,y2i,z2i],
[x3i,y3i,z3i],
[x4i,y4i,z4i]]
verts =
[[ 0. 5. 1.]
[ 0. 5. 0.]
[ 1. 5. 1.]
[ 1. 5. 0.]
[ 0. 5. 2.]]
```

if the surface has five vertices.
`faces`

is a list of the indices of the vertices that make up the triangles of the surface.

```
faces =
[[v0i,v1i,v2i],
[v3i,v4i,v5i],
... #3 hidden rows
[v2i,v1i,v0i]]
faces =
[[ 2 1 0]
[ 2 3 1]
[ 1 3 2]
[ 0 4 2]
[ 2 3 1]
[ 0 1 2]]
```

if the surface has six faces.
In other words, if `faces[0] = [2, 1, 0]`

, then it represents the triangle given by the three vertices

Now, it is useful to calculate the position of a point located on the surface, which is calculated by the line

which gives the position of the centroids of the triangles that make up the surface.

```
centroids =
[[x0c,y0c,z0c],
[x1c,y1c,z1c],
[x2c,y2c,z2c],
[x3c,y3c,z3c],
[x4c,y4c,z4c]
[x5c,y5c,z5c]]
centroids =
[[0.33333334 5. 0.6666667 ]
[0.6666667 5. 0.33333334]
[0.33333334 5. 1.6666666 ]
[0.6666667 5. 1.3333334 ]
[0.33333334 5. 2.6666667 ]
[0.6666667 5. 2.3333333 ]]
```

As we see, the centroids array consists of as many rows as there are faces (naturally), and each row consists of the x-, y- and z-coordinates of the centroid of the corresponding face.

In the next line, we typically create the `points`

array, as follows:

```
x, y, z = np.mgrid[0:field.shape[0], 0:field.shape[1], 0:field.shape[2]]
points = np.c_[x.ravel(), y.ravel(), z.ravel()]
```

The `points`

array is a list of the (integer) positions of all the points in the full 3D grid.

```
points =
[[x0i,y0i,z0i],
[x1i,y1i,z1i],
[x2i,y2i,z2i],
[x3i,y3i,z3i],
[x4i,y4i,z4i],
... # 300 rows total
[x299i,y299i,z299i]]
points =
[[ 0 0 0]
[ 0 0 1]
[ 0 0 2]
... # 300 rows total
[10 10 8]
[10 10 9]
[10 10 10]]
```

Then, we create the `field_values`

array by

which is a `(300,)`

-shaped array of the values of the field at the points in the `points`

array, i.e., in the full grid.

Now we get to the interpolation, which happens by the command

which uses the information in `points`

and `field_values`

to interpolate the field values at the centroids of the faces of the surface.
It returns thus a `(6,)`

-array containing the field values to be used in the plotting of the surface.
The `nearest`

method is used to interpolate the field values, which means that the field value at the centroid of a face is the field value of the point in the full grid that is closest to the centroid of the face.