Pairing2D

This example demonstrates the freud.environment.Pairing2D module.

In [1]:
from bokeh.resources import INLINE
from bokeh.io import output_notebook
output_notebook(resources=INLINE)
from bokeh.plotting import figure, output_file, show
from bokeh.layouts import gridplot
import numpy as np
from util import local_to_global, default_bokeh
from freud import box
from freud import environment

verts = [[-0.25, -0.5],
         [0.25, -0.5],
         [0.25, 0.5],
         [-0.25, 0.5]]
c_list = ["#30A2DA", "#FC4F30", "#E5AE38", "#6D904F", "#9757DB",
          "#188487", "#FF7F00", "#9A2C66", "#626DDA", "#8B8B8B"]
c_dict = dict()
c_dict[6] = c_list[0]
c_dict[5] = c_list[1]
c_dict[4] = c_list[2]
c_dict[3] = c_list[7]
c_dict[7] = c_list[4]
Loading BokehJS ...

Create Example System and Visualize

We will create a three particle system of rectangles and visualize it.

In [2]:
# import freud box class
# create data
fbox = box.Box(Lx=10, Ly=10, is2D=True)
pos = np.array([[-1, 0, 0], [0, 0, 0], [1, 0, 0]], dtype=np.float32)
ang = np.array([0.0, 0.0, 0.0], dtype=np.float32)

side_length = max(fbox.Lx, fbox.Ly)
l_min = -side_length / 2.0
l_min *= 1.1
l_max = -l_min

p = figure(title="System Visualization", x_range=(l_min, l_max), y_range=(l_min, l_max))
patches = local_to_global(verts, pos[:,0:2], ang)
for i in range(3):
    fill_color = np.array([c_list[i] for _ in range(1)])
    l_patches = np.zeros(shape=(1,patches.shape[1],patches.shape[2]), dtype=patches.dtype)
    l_patches[0] = patches[i]
    p.patches(xs=l_patches[:,:,0].tolist(),
              ys=l_patches[:,:,1].tolist(),
              fill_color=fill_color.tolist(),
              line_color="black",
              line_width=1.5,
              legend="{}".format(i))
p.patches(xs=[[-fbox.Lx/2, fbox.Lx/2, fbox.Lx/2, -fbox.Lx/2]],
          ys=[[-fbox.Ly/2, -fbox.Ly/2, fbox.Ly/2, fbox.Ly/2]],
          fill_color=(0,0,0,0), line_color="black", line_width=2)
p.legend.location='bottom_center'
p.legend.orientation='horizontal'
default_bokeh(p)
show(p)

Compute Pairing

We use the freud.environment.Pairing2D module. We set the max distance to search for neighbors, the number of neighbors to search, and the tolerance for the dot product used in the computation.

In [3]:
# pair stuff
myPair = environment.Pairing2D(rmax=1.1, k=2, compDotTol=0.1)

Create array of angles to search for pairs

Not all shapes have one orientation where a pair could form. In this system a paired rectangle could be either to the left or right i.e. \(\theta=0\) or \(\theta=\pi\). To account for this, we create an array that gives every particle a set of local orientations to search for a pair, in this case \(\theta=0\) or \(\theta=\pi\).

In [4]:
c_ang = np.zeros(shape=(pos.shape[0],2), dtype=np.float32)
c_ang[:,1] = np.pi
myPair.compute(fbox, pos, ang, c_ang)
Out[4]:
<freud.environment.Pairing2D at 0x7f32c00ae188>

Investigate output

Two arrays are provided as output: the match array and the pair array.

Match array

The match array is a boolean array that is \(1\) if a particle is matched and \(0\) if not.

Pair array

The pair array gives the index of the paired particle. Note that if a particle could be paired to multiple particles, only one pair is returned, the lower of the two index values.

In this example, particle 0 is paired with particle 1, particle 1 is paired with particle 0 (and technically 2), and particle 2 is paired with particle 1.

In [5]:
x = myPair.match
print(x)
y = myPair.pair
print(y)
[1 1 1]
[1 0 1]

Other examples

In [6]:
pos = np.array([[-1.5, 0, 0], [0, 0, 0], [1, 0, 0]], dtype=np.float32)
ang = np.array([0.0, 0.0, 0.0], dtype=np.float32)

side_length = max(fbox.Lx, fbox.Ly)
l_min = -side_length / 2.0
l_min *= 1.1
l_max = -l_min

p = figure(title="System Visualization", x_range=(l_min, l_max), y_range=(l_min, l_max))
patches = local_to_global(verts, pos[:,0:2], ang)
for i in range(3):
    fill_color = np.array([c_list[i] for _ in range(1)])
    l_patches = np.zeros(shape=(1,patches.shape[1],patches.shape[2]), dtype=patches.dtype)
    l_patches[0] = patches[i]
    p.patches(xs=l_patches[:,:,0].tolist(),
              ys=l_patches[:,:,1].tolist(),
              fill_color=fill_color.tolist(),
              line_color="black",
              line_width=1.5,
              legend="{}".format(i))
p.patches(xs=[[-fbox.Lx/2, fbox.Lx/2, fbox.Lx/2, -fbox.Lx/2]],
          ys=[[-fbox.Ly/2, -fbox.Ly/2, fbox.Ly/2, fbox.Ly/2]],
          fill_color=(0,0,0,0), line_color="black", line_width=2)
p.legend.location='bottom_center'
p.legend.orientation='horizontal'
default_bokeh(p)
show(p)

myPair.compute(fbox, pos, ang, c_ang)
x = myPair.match
print(x)
y = myPair.pair
print(y)
[0 1 1]
[0 2 1]

Now that we move particle 0 farther away, it is no longer paired, and particles 1 and 2 are paired.

In [7]:
pos = np.array([[-1.0, 0, 0], [0, 0, 0], [1, 0, 0]], dtype=np.float32)
ang = np.array([0.1, 0.0, 0.0], dtype=np.float32)

side_length = max(fbox.Lx, fbox.Ly)
l_min = -side_length / 2.0
l_min *= 1.1
l_max = -l_min

p = figure(title="System Visualization", x_range=(l_min, l_max), y_range=(l_min, l_max))
patches = local_to_global(verts, pos[:,0:2], ang)
for i in range(3):
    fill_color = np.array([c_list[i] for _ in range(1)])
    l_patches = np.zeros(shape=(1,patches.shape[1],patches.shape[2]), dtype=patches.dtype)
    l_patches[0] = patches[i]
    p.patches(xs=l_patches[:,:,0].tolist(),
              ys=l_patches[:,:,1].tolist(),
              fill_color=fill_color.tolist(),
              line_color="black",
              line_width=1.5,
              legend="{}".format(i))
p.patches(xs=[[-fbox.Lx/2, fbox.Lx/2, fbox.Lx/2, -fbox.Lx/2]],
          ys=[[-fbox.Ly/2, -fbox.Ly/2, fbox.Ly/2, fbox.Ly/2]],
          fill_color=(0,0,0,0), line_color="black", line_width=2)
p.legend.location='bottom_center'
p.legend.orientation='horizontal'
default_bokeh(p)
show(p)

myPair.compute(fbox, pos, ang, c_ang)
x = myPair.match
print(x)
y = myPair.pair
print(y)
[0 1 1]
[0 2 1]

Now we rotate particle 0 so that it’s no longer within the dot product tolerance for being bound

In [8]:
# set a higher tolerance
myPair = environment.Pairing2D(rmax=1.1, k=2, compDotTol=0.5)
pos = np.array([[-1.0, 0, 0], [0, 0, 0], [1, 0, 0]], dtype=np.float32)
ang = np.array([0.1, 0.0, 0.0], dtype=np.float32)

side_length = max(fbox.Lx, fbox.Ly)
l_min = -side_length / 2.0
l_min *= 1.1
l_max = -l_min

p = figure(title="System Visualization", x_range=(l_min, l_max), y_range=(l_min, l_max))
patches = local_to_global(verts, pos[:,0:2], ang)
for i in range(3):
    fill_color = np.array([c_list[i] for _ in range(1)])
    l_patches = np.zeros(shape=(1,patches.shape[1],patches.shape[2]), dtype=patches.dtype)
    l_patches[0] = patches[i]
    p.patches(xs=l_patches[:,:,0].tolist(),
              ys=l_patches[:,:,1].tolist(),
              fill_color=fill_color.tolist(),
              line_color="black",
              line_width=1.5,
              legend="{}".format(i))
p.patches(xs=[[-fbox.Lx/2, fbox.Lx/2, fbox.Lx/2, -fbox.Lx/2]],
          ys=[[-fbox.Ly/2, -fbox.Ly/2, fbox.Ly/2, fbox.Ly/2]],
          fill_color=(0,0,0,0), line_color="black", line_width=2)
p.legend.location='bottom_center'
p.legend.orientation='horizontal'
default_bokeh(p)
show(p)

myPair.compute(fbox, pos, ang, c_ang)
x = myPair.match
print(x)
y = myPair.pair
print(y)
[1 1 1]
[1 0 1]

When we increase the dot product tolerance, particle 0 and particle 1 are paired once again