Skip to content

Commit 8ceec70

Browse files
committed
Added particles functions
1 parent 19cbe51 commit 8ceec70

2 files changed

Lines changed: 228 additions & 0 deletions

File tree

particles/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""
2+
Particles routines
3+
4+
"""
5+
6+
__all__ = ["particles"]

particles/particles.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
"""
2+
Stores and manages particles and updates their positions based
3+
on the velocity on the grid.
4+
"""
5+
6+
import numpy as np
7+
import mesh.reconstruction as reconstruction
8+
from util import msg
9+
10+
11+
class Particle(object):
12+
13+
def __init__(self, x, y, u=0, v=0, mass=1):
14+
self.x = x
15+
self.y = y
16+
self.u = u
17+
self.v = v
18+
self.mass = mass
19+
20+
def pos(self):
21+
"""
22+
Return position vector.
23+
"""
24+
return np.array([self.x, self.y])
25+
26+
def velocity(self):
27+
"""
28+
Return velocity vector.
29+
"""
30+
return np.array([self.u, self.v])
31+
32+
def advect(self, u, v, dt):
33+
"""
34+
Advect the particle and update its velocity.
35+
"""
36+
self.u = u
37+
self.v = v
38+
self.x += u * dt
39+
self.y += v * dt
40+
41+
42+
class Particles(object):
43+
44+
def __init__(self, sim_data, bc, n_particles=100):
45+
"""
46+
Initialize the Particles object.
47+
48+
Parameters
49+
----------
50+
sim_data : CellCenterData2d object
51+
The simulation data
52+
"""
53+
54+
self.sim_data = sim_data
55+
self.bc = bc
56+
57+
# TODO: read something from rp here to determine how to
58+
# generate the particles - for now, we shall assume random.
59+
60+
self.randomly_generate_particles(n_particles)
61+
62+
def randomly_generate_particles(self, n_particles):
63+
"""
64+
Randomly generate n_particles.
65+
"""
66+
myg = self.sim_data.grid
67+
68+
positions = np.random.rand(n_particles, 2)
69+
70+
positions[:, 0] = positions[:, 0] * (myg.xmax - myg.xmin) + \
71+
myg.xmin
72+
73+
positions[:, 1] = positions[:, 1] * (myg.ymax - myg.ymin) + \
74+
myg.ymin
75+
76+
self.particles = set([Particle(x, y) for (x, y) in positions])
77+
78+
def update_particles(self, u, v, dt, limiter=0):
79+
"""
80+
Update the particles on the grid. To do this, we need to
81+
calculate the velocity at the particle's position (do we
82+
do this by interpolating - ie assuming the grid velocities
83+
to live at points - or by using the velocity in the cell well
84+
the particle is - ie assuming the grid velocities to live in the
85+
entire cell. I think I'll try using the same projection methods
86+
used in other codes?)
87+
88+
We will explicitly pass in u and v here as these are accessed
89+
differently in different problems.
90+
"""
91+
myg = self.sim_data.grid
92+
myd = self.sim_data.data
93+
94+
# limit the velocity
95+
96+
ldelta_ux = reconstruction.limit(u, myg, 1, limiter)
97+
ldelta_uy = reconstruction.limit(u, myg, 2, limiter)
98+
99+
ldelta_vx = reconstruction.limit(v, myg, 1, limiter)
100+
ldelta_vy = reconstruction.limit(v, myg, 2, limiter)
101+
102+
for p in self.particles:
103+
# find what cell it lives in
104+
x_idx = (p.x - myg.xmin) / myg.dx - 0.5
105+
y_idx = (p.y - myg.ymin) / myg.dy - 0.5
106+
107+
x_frac = x_idx % 1
108+
y_frac = y_idx % 1
109+
110+
x_idx = int(round(x_idx))
111+
y_idx = int(round(y_idx))
112+
113+
if x_frac > 0.5 and x_idx+1 < myg.nx:
114+
x_frac -= 1
115+
x_idx += 1
116+
if y_frac > 0.5 and y_idx+1 < myg.ny:
117+
y_frac -= 1
118+
y_idx += 1
119+
120+
if x_idx >= myg.nx:
121+
x_frac += (x_idx - myg.nx) + 1
122+
x_idx = myg.nx - 1
123+
if y_idx >= myg.ny:
124+
y_frac += (y_idx - myg.ny) + 1
125+
y_idx = myg.ny - 1
126+
127+
u_vel = u.v()[x_idx, y_idx]
128+
v_vel = v.v()[x_idx, y_idx]
129+
cx = u_vel * dt / myg.dx
130+
cy = v_vel * dt / myg.dy
131+
132+
# normal velocity
133+
if (u_vel*x_frac) < 0:
134+
u_vel -= x_frac*(1.0 + cx)*ldelta_ux.v()[x_idx, y_idx]
135+
else:
136+
u_vel += x_frac*(1.0 - cx)*ldelta_ux.v()[x_idx, y_idx]
137+
138+
if (v_vel*y_frac) < 0:
139+
v_vel -= y_frac*(1.0 + cy)*ldelta_vy.v()[x_idx, y_idx]
140+
else:
141+
v_vel += y_frac*(1.0 - cy)*ldelta_vy.v()[x_idx, y_idx]
142+
143+
# transverse velocity
144+
u_vel += y_frac / myg.dy * ldelta_uy.v()[x_idx, y_idx]
145+
v_vel += x_frac / myg.dx * ldelta_vx.v()[x_idx, y_idx]
146+
147+
p.advect(u_vel, v_vel, dt)
148+
149+
def enforce_particle_boundaries(self):
150+
"""
151+
Enforce the particle boundaries
152+
153+
TODO: copying the set and adding everything back again is messy
154+
- think of a better way to do this?
155+
"""
156+
new_particles = self.particles.copy()
157+
self.particles = set()
158+
159+
myg = self.sim_data.grid
160+
161+
xlb = self.bc.xlb
162+
xrb = self.bc.xrb
163+
ylb = self.bc.ylb
164+
yrb = self.bc.yrb
165+
166+
while new_particles:
167+
p = new_particles.pop()
168+
169+
# -x boundary
170+
if xlb == "outflow":
171+
if p.x < myg.xmin:
172+
continue
173+
elif xlb == "periodic":
174+
if p.x < myg.xmin:
175+
p.x = myg.xmax + p.x - myg.xmin
176+
else:
177+
msg.fail("ERROR: xlb = %s invalid BC" % (xlb))
178+
179+
# +x boundary
180+
if xrb == "outflow":
181+
if p.x > myg.xmax:
182+
continue
183+
elif xrb == "periodic":
184+
if p.x > myg.xmax:
185+
p.x = myg.xmin + p.x - myg.xmax
186+
else:
187+
msg.fail("ERROR: xrb = %s invalid BC" % (xrb))
188+
189+
# -y boundary
190+
if ylb == "outflow":
191+
if p.y < myg.ymin:
192+
continue
193+
elif ylb == "periodic":
194+
if p.y < myg.ymin:
195+
p.y = myg.ymax + p.y - myg.ymin
196+
else:
197+
msg.fail("ERROR: ylb = %s invalid BC" % (ylb))
198+
199+
# +x boundary
200+
if yrb == "outflow":
201+
if p.y > myg.ymax:
202+
continue
203+
elif yrb == "periodic":
204+
if p.y > myg.ymax:
205+
p.y = myg.ymin + p.y - myg.ymax
206+
else:
207+
msg.fail("ERROR: yrb = %s invalid BC" % (yrb))
208+
209+
self.particles.add(p)
210+
211+
def get_positions(self):
212+
"""
213+
Return an array of particle positions.
214+
"""
215+
return np.array([[p.x, p.y] for p in self.particles])
216+
217+
def write_particles(self, filename):
218+
"""
219+
Output the particles to an HDF5 file
220+
"""
221+
222+
pass

0 commit comments

Comments
 (0)