Skip to content

Commit ef40e54

Browse files
committed
Merge pull request #3 from dwieker/nkeim/add-gofr-rebased
Finished 3D g(r)
2 parents a0a0521 + db1fb85 commit ef40e54

2 files changed

Lines changed: 33 additions & 70 deletions

File tree

trackpy/static.py

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from warnings import warn
55

66

7-
87
def pairCorrelationKDTree2D(feat, cutoff, fraction = 1., dr = .5, p_indices = None, ndensity=None, boundary = None,
98
handle_edge=True):
109
"""
@@ -172,12 +171,8 @@ def pairCorrelationKDTree3D(feat, cutoff, fraction = 1., dr = .5, p_indices = No
172171
for idx in p_indices:
173172
dist, idxs = ckdtree.query(points[idx], k=max_p_count, distance_upper_bound=cutoff)
174173
dist = dist[dist > 0] # We don't want to count the same particle
175-
#print dist.shape
176-
#print dist[dist.shape[0] - 10:]
177-
174+
178175
area = (4./3.) * np.pi * (np.arange(dr, cutoff + 2*dr, dr)**3 - np.arange(0, cutoff + dr, dr)**3)
179-
180-
#print area
181176

182177
if handle_edge:
183178
# Find the number of edge collisions at each radii
@@ -188,58 +183,40 @@ def pairCorrelationKDTree3D(feat, cutoff, fraction = 1., dr = .5, p_indices = No
188183

189184
# Use analyitcal solution to find area of disks cut off by one wall.
190185
# Grab the distance to the closest wall
191-
#d = _distances_to_wall3D(points[idx], xmin, xmax, ymin, ymax, zmin, zmax).min()
192-
193-
#inx = np.where(collisions == 1)[0]
194-
195-
#theta = np.arccos(d / (r_edges[inx] + dr/2))
196-
#area[inx] *= 1 - 2*np.pi*(1 - np.cos(theta)) / (4*np.pi)
186+
d = _distances_to_wall3D(points[idx], xmin, xmax, ymin, ymax, zmin, zmax).min()
187+
inx = np.where(collisions == 1)[0]
188+
theta = np.arccos(d / (r_edges[inx] + dr/2))
189+
area[inx] *= 1 - 2*np.pi*(1 - np.cos(theta)) / (4*np.pi)
197190

198191
# If shell is cutoff by 2 or more walls, generate a bunch of points and use a mask to
199192
# estimate the area within the boundaries
200-
inx = np.where(collisions >= 1)[0]
193+
inx = np.where(collisions >= 2)[0]
201194
x = refx[inx] + points[idx,0]
202195
y = refy[inx] + points[idx,1]
203196
z = refz[inx] + points[idx,2]
204197
mask = (x >= xmin) & (x <= xmax) & (y >= ymin) & (y <= ymax) & (z >= zmin) & (z <= zmax)
205198
area[inx] *= mask.sum(axis=1, dtype='float') / len(refx[0])
206199

207-
print points[idx]
208-
print mask.sum(axis=1, dtype='float') / len(refx[0])
209-
210200
g_r += np.histogram(dist, bins = r_edges)[0] / area[:-1]
211201

212202
g_r /= (ndensity * len(p_indices))
213203
return r_edges, g_r
214204

215205
def _num_wall_collisions2D(point, radius, xmin, xmax, ymin, ymax):
206+
"""Returns the number of walls a shell of a certain radius and position collides with.
207+
Wall boundaries specified by min, max parameters"""
216208
collisions = (point[0] + radius >= xmax).astype(int) + (point[0] - radius <= xmin).astype(int) + \
217209
(point[1] + radius >= ymax).astype(int) + (point[1] - radius <= ymin).astype(int)
218210

219211
return collisions
220212

221213
def _distances_to_wall2D(point, xmin, xmax, ymin, ymax):
214+
"""Returns the distance of a paritlce a position 'point' to the nearest wall"""
222215
return np.array([point[0]-xmin, xmax-point[0], point[1]-ymin, ymax-point[1]])
223216

224217
def _points_ring2D(r_edges, dr, n):
225218
"""Returns x, y array of points comprising shells extending from r to r_dr.
226-
227-
layers determines how many concentric layers are in each shell,
228-
and n determines the number of points in each layer"""
229-
230-
"""
231-
refx=np.empty((len(r_edges), n*layers))
232-
refy=refx.copy()
233-
for index, r in enumerate(r_edges):
234-
theta = np.linspace(0, 2*np.pi, n)
235-
theta = theta.repeat(layers).reshape((len(theta), layers))
236-
x = np.cos(theta) * np.linspace(r, r+dr, layers)
237-
y = np.sin(theta) * np.linspace(r, r+dr, layers)
238-
refx[index] = x.reshape(n*layers)
239-
refy[index] = y.reshape(n*layers)
240-
241-
return refx, refy
242-
"""
219+
n determines the number of points in each ring"""
243220

244221
refx_all, refy_all = [],[]
245222
for r in r_edges:
@@ -255,17 +232,21 @@ def _points_ring2D(r_edges, dr, n):
255232

256233

257234
def _num_wall_collisions3D(point, radius, xmin, xmax, ymin, ymax, zmin, zmax):
235+
"""Returns the number of walls a shell of a certain radius and position collides with.
236+
Wall boundaries specified by min, max parameters"""
258237
collisions = (point[0] + radius >= xmax).astype(int) + (point[0] - radius <= xmin).astype(int) + \
259238
(point[1] + radius >= ymax).astype(int) + (point[1] - radius <= ymin).astype(int) + \
260239
(point[2] + radius >= zmax).astype(int) + (point[2] - radius <= zmin).astype(int)
261240

262241
return collisions
263242

264243
def _distances_to_wall3D(point, xmin, xmax, ymin, ymax, zmin, zmax):
244+
"""Returns the distance of a paritlce a position 'point' to the nearest wall"""
265245
return np.array([point[0]-xmin, xmax-point[0], point[1]-ymin, ymax-point[1], point[2]-zmin, zmax-point[2]])
266246

267247
def _points_ring3D(r_edges, dr, n):
268-
"""Returns x, y, z arrays of points comprising shells extending from r to r_dr. n determines the density of the shells"""
248+
"""Returns x, y, z arrays of points comprising shells extending from r to r_dr.
249+
n determines the number of particles in each of the shells"""
269250

270251
refx_all, refy_all, refz_all = [],[],[]
271252
for r in r_edges:

trackpy/tests/test_static.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
class TestPairCorrelation(unittest.TestCase):
1111

1212

13-
def test_correlation2D(self):
14-
15-
##### 2D TEST ######
13+
def test_correlation2D_lattice(self):
14+
### Lattice Test
15+
# With proper edge handling, g(r) of the particle at the center should be the same as g(r) for all particles.
1616
lattice = self._lattice2D()
1717

1818
# Calculate g_r on the center particle only (index 210)
@@ -34,6 +34,10 @@ def test_correlation2D(self):
3434
self.assertFalse(np.allclose(g_r_all, g_r_no_edge, atol=.02))
3535

3636

37+
def test_correlation2D_ring(self):
38+
# Ring test
39+
# Generate a series of concentric shells, each with the same number of particles.
40+
# The peaks in g(r) should decay as 1/r.
3741
ring = self._rings2D()
3842

3943
edges, g_r = pairCorrelationKDTree2D(ring, dr=.1, cutoff=10, p_indices=[0], boundary = (-10., 10., -10., 10.))
@@ -53,65 +57,40 @@ def test_correlation3D_ring(self):
5357
# Generate a series of concentric shells, each with the same number of particles.
5458
# The peaks in g(r) should decay as 1/r^2.
5559
ring = self._rings3D()
56-
5760
edges, g_r = pairCorrelationKDTree3D(ring, dr=.1, cutoff=10, p_indices=[len(ring) - 1], boundary = (-10., 10., -10., 10., -10., 10.), handle_edge=True)
5861
g_r /= np.linalg.norm(g_r)
5962
peaks = g_r[g_r > 0]
60-
6163
assert len(peaks) == 9
62-
6364
x = np.arange(1,10,1)
6465
r = peaks.max() * 1/x**2
65-
6666
self.assertTrue( np.allclose(peaks, r, atol=.02) )
6767

6868

6969
def test_correlation3D_lattice(self):
7070
### Lattice Test
7171
# With proper edge handling, g(r) of the particle at the center should be the same as g(r) for all particles.
72-
lattice = self._lattice3D(n = 10)
73-
74-
print lattice.iloc[444]
72+
lattice = self._lattice3D(n = 20)
73+
7574
# Calculate g_r on the center particle only (index 210)
76-
edges, g_r_one = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=4, p_indices=[444])
75+
edges, g_r_one = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=7, p_indices=[4649])
7776
g_r_one /= np.linalg.norm(g_r_one) #We care about the relative difference of g_r in this case, so let's normalize both.
7877

7978
# Calculate g_r on all particles
80-
edges, g_r_all = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=4)
79+
edges, g_r_all = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=7)
8180
g_r_all /= np.linalg.norm(g_r_all)
8281

8382
# Calculate g_r on all particles
84-
edges, g_r_no_edge = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=4, handle_edge=False)
83+
edges, g_r_no_edge = pairCorrelationKDTree3D(lattice, dr=.1, cutoff=7, handle_edge=False)
8584
g_r_no_edge /= np.linalg.norm(g_r_no_edge)
8685

87-
88-
89-
plt.plot(edges[:-1], g_r_one, label='one')
90-
plt.plot(edges[:-1], g_r_all, label='all')
91-
plt.plot(edges[:-1], g_r_no_edge, label='all, no edge')
92-
plt.legend(loc='best')
93-
plt.show()
94-
9586
# Assert the functions are essentially the same
96-
self.assertTrue(np.allclose(g_r_all, g_r_one, atol=.02))
87+
self.assertTrue(np.allclose(g_r_all, g_r_one, atol=.04))
9788

9889
# Turning off edge handling should give incorrect result
99-
self.assertFalse(np.allclose(g_r_all, g_r_no_edge, atol=.02))
100-
101-
def test_sphere_mask(self):
102-
x,y,z = _points_ring3D([1], 0, 10000)
103-
#plt.scatter(x,y)
104-
#plt.show()
105-
106-
mask= (x >= 0) & (x <= 1) & (y >= 0) & (y <= 1)# & (z >= 0) & (z <= 1)
107-
print mask.sum() / len(x)
108-
#plt.scatter(x[mask], y[mask])
109-
#plt.show()
110-
111-
112-
90+
self.assertFalse(np.allclose(g_r_all, g_r_no_edge, atol=.04))
11391

11492
def _lattice2D(self, n = 20):
93+
#Generates 2D lattice, spacing = 1
11594
x,y = [],[]
11695
epsilon = 0.0
11796
for i in range(n):
@@ -123,6 +102,7 @@ def _lattice2D(self, n = 20):
123102

124103

125104
def _rings2D(self):
105+
#Generates concentric rings, with a particle at the center
126106
theta = np.linspace(0, 2*np.pi, 10)
127107
points = np.zeros((100,2))
128108

@@ -138,6 +118,7 @@ def _rings2D(self):
138118

139119

140120
def _lattice3D(self, n = 20):
121+
#Generates 3D lattice, spacing = 1
141122
x,y,z = [],[],[]
142123
for i in range(n):
143124
for j in range(n):
@@ -150,6 +131,7 @@ def _lattice3D(self, n = 20):
150131

151132

152133
def _rings3D(self):
134+
#Generates concentric spherical shells, with a particle at the center
153135
epsilon = .02
154136
r = np.arange(1, 10, 1) + epsilon
155137
refx, refy, refz = _points_ring3D(r, 0, 500)

0 commit comments

Comments
 (0)