I've written a 0th order Brownian motion simulator to envision how a particle of smoke might appear to move under a microscope.
There will be missing $\sqrt{2}$'s and $\frac{\pi}{2}$'s since I haven't done proper averaging over phase space and the Maxwell-Boltzman distribution of molecule velocities in 3D.
Question: But my question is about viscosity. If I increase the number density, the displacement and velocity of the particles also increase without limit, because there's no viscosity term. How would I add viscous damping to this collision-based model? Is it possible do to so with just the mass and temperature of the air molecules and the smoke particle, and not use a "book value" for viscosity?
hunch: I wonder if I need to add a small $\Delta v$ to the flux $n_0 v_0$ and subtract it on the opposite side? Or do I need to handle that inside the integral over the Maxwell-Boltzman distribution, which I have avoided so far by using an average velocity.
This answer is far too advanced for this question, and this answer not advanced enough, though it shows a nice simulation GIF and links to here and here.
At each time step I choose the momentum transfer to the smoke particle from a normal distribution with
$$\sigma_p = m_0 v_0 \sqrt{n}$$
and
$$v_0=\sqrt{k_BT/m_0},$$
$$n=n_0 v_0 A\Delta t$$
where $n$ is the number of collisions during the period $\Delta t$. $m_0$ and $m_p$ are the masses of air molecules and the smoke particle, respectively, and $A$ is the projected area of the particle.
At each step the change in the smoke particle's velocity is given by
$$dv = dp/m_p$$
At a point in time $t_j$ the velocity would be the cumulative sum of $dv \Delta T$,
$$v_j = \Delta t \sum_{i=0}^j dv_i$$
and the position another cumulative sum of velocity:
$$x_j = \Delta t \sum_{i=0}^j v_i$$
I get plots that look plausible. Below is a 2 micron particle of porous carbon after 10,000 steps of 0.001 second each.
Python script:
import numpy as np
import matplotlib.pyplot as plt
kB = 1.381E-23
n0 = 0.02504E+27 # m^-3
m0 = 28 * 1.67E-27 # kg
T = 293. # K
v0 = np.sqrt(kB*T/m0) # m/s
p0 = m0*v0
Area = 4E-12 # m^2 (2x2 microns)
flux = Area * n0 * v0
tstep = 0.001
n = flux * tstep
sigma_p = p0 * np.sqrt(n)
N = 10000
time = tstep*np.arange(N)
dp = np.random.normal(0, sigma_p, 3*N).reshape(3, -1)
rhop = 1000. # kg/m^3 porous carbon
Vp = 8E-18 # m^3 (2x2x2 microns)
mp = rhop*Vp
dv = dp/mp
vel = dv.cumsum(axis=-1) * tstep
pos = vel.cumsum(axis=-1) * tstep
def squareax(ax):
(xmin, xmax), (ymin, ymax) = ax.get_xlim(), ax.get_ylim()
xc, yc = 0.5*(xmax+xmin), 0.5*(ymax+ymin)
xw, yw = xmax-xmin, ymax-ymin
hw = 1.1 * 0.5 * max(xw, yw)
if True:
fig = plt.figure()
velplt, posplt = 1000.*vel, 1000.*pos
ax1 = fig.add_subplot(2, 2, 1)
for thing in posplt:
ax1.plot(time, thing)
plt.title('x, y, z (mm) vs. time (sec)')
ax2 = fig.add_subplot(2, 2, 2)
ax2.plot(posplt[0], posplt[1])
squareax(ax2)
plt.title('y vs. x (mm)')
ax3 = fig.add_subplot(2, 2, 3)
for thing in velplt:
ax3.plot(time, thing)
plt.title('vx, vy, vz (mm/s) vs time (sec)')
ax4 = fig.add_subplot(2, 2, 4)
ax4.plot(velplt[0], velplt[1])
squareax(ax4)
plt.title('vy vs. vx (mm/s)')
plt.show()