A walker that takes one random step at a time, the diffusion law that many of them obey, and the diffusion coefficient measured from the walk and checked against the value the step rule predicts.
With scripts, this page keeps a record in your browser of whether you completed the lesson. Nothing is stored without them.
With scripts, this place suggests what to do next, with the reason, from the progress saved in your browser. Without them, follow the learning path.
A drop of ink in still water spreads, though no current carries it. A smell crosses a room faster than the air moves. Nothing pushes the ink or the smell in any direction: each molecule is knocked about by its neighbours, and the spreading belongs to the crowd. How fast does the crowd spread, and what does that speed have to do with the knocks?
This lesson builds the smallest model of the knocks: a walker that takes one step of a fixed length in a random direction, again and again, each step independent of the last. It is the state and the update rule of the kinematics lesson, with a random velocity in place of a given one. No rule mentions spreading. The lesson then measures how far many walkers have gone from the start, finds that the square of the distance grows in proportion to the time, and reads off a diffusion coefficient. The step rule predicts that coefficient, so the measurement has something to be checked against. The checks are statistical, because the walk is random, and they use the tolerance of the gas lesson: four standard errors over independent runs. The data are simulated; no real ink is measured.
Assumptions
Equal steps at equal times. Every step has the same length and takes the same time . Real molecules have a spread of free paths and of times between collisions. The coefficient below then depends on the mean squared step and the mean time, which is what the formula uses when the steps are equal.
Directions are uniform and independent. Each step points in a direction drawn uniformly from the circle, whatever the earlier steps were. A walker that tends to continue in the direction it was going (a persistent walk) spreads faster than this formula says, and a walker that is pulled back to a point does not spread without limit.
No walker affects another. The walkers do not collide and do not see each other, so the average over them is an average over independent copies. A crowd that excludes itself (a dense gas) has a different diffusion coefficient for the same step.
Many steps. The diffusion law and the Gaussian spread are the limit of many steps. After a few steps the walkers sit on a few circles around the start, and the Gaussian is a poor description.
Randomness is seeded. A generator created from a seed makes the walks, so a run is a function of its inputs and the page is the same at every build.
The runs are independent, the steps are not walkers. The standard error of a measured quantity is computed from the spread between independent runs with different seeds. The walkers inside one run are independent too, but a quantity that is read off one walker at successive times is correlated with itself, and the lesson never treats those readings as independent.
Explanation
From one rule to the mean squared displacement
The state of a walker is its position after steps. One step is
with a unit vector in a random direction. Start at the origin and add the steps: . The square of the distance is the sum of every product of two steps,
The mean of over independent uniform directions is zero when , because a direction and its opposite are equally likely, and it is one when . Only the terms of the diagonal survive:
The mean squared displacement grows in proportion to time. That is the diffusion law, and it is a property of the crowd: the distance of one walker grows like on average, not like .
The diffusion coefficient
In dimensions the law is written , which defines the diffusion coefficient . With the result above,
so in the plane . The coefficient has the unit of , and it is the only number the law needs: a larger step or a shorter time between steps spreads the walkers faster.
The shape of the spread
Each coordinate of is a sum of many independent terms of mean zero, so for many steps it is Gaussian by the central limit theorem, with variance in the plane. The distance then follows the Rayleigh distribution, and the probability that a walker is within a radius of the start is the integral of the Gaussian over the disc,
This is a second check, on the shape and not only on the second moment. It uses the same .
What the tolerance means
A run gives one value of and another seed gives another. With independent runs, the mean estimates and the standard error , with the sample standard deviation, says how far the mean is likely to be from the value the model gives for unlimited runs. A result agrees with the prediction within standard errors if the difference is at most times the standard error. Four is a chosen tolerance, not a law, and with only six runs the standard error is itself uncertain, so the discrepancy follows a Student distribution with five degrees of freedom: chance alone exceeds four standard errors in about one case in a hundred. A failure is a reason to look at the model or the program, and a repeat with other seeds tells the two apart. A model that is wrong by more than the tolerance is caught, and one that is wrong by less is not.
Code
The walk is pbc.thermodynamics.diffusion. random_walks draws every step of every walker at once from a seeded generator, and mean_squared_displacement averages the squared distance over the walkers.
def random_walks( n_walkers: int, n_steps: int, step_length: float, rng: np.random.Generator, dimension: int=2,) -> Array:"""The positions (m) of ``n_walkers`` walkers that start at the origin, after 0, 1, ..., ``n_steps`` steps: an array of shape ``(n_steps + 1, n_walkers, dimension)``. Each step has length ``step_length`` and a direction uniform on the circle (``dimension`` 2) or on the line (``dimension`` 1, a step left or right). """if n_walkers <1or n_steps <1or step_length <=0:raiseValueError("n_walkers and n_steps must be at least 1, step_length positive" )if dimension ==1: steps = rng.choice([-1.0, 1.0], size=(n_steps, n_walkers, 1))elif dimension ==2: angles = rng.uniform(0.0, 2.0* np.pi, size=(n_steps, n_walkers)) steps = np.stack([np.cos(angles), np.sin(angles)], axis=-1)else:raiseValueError("dimension must be 1 or 2") paths = np.zeros((n_steps +1, n_walkers, dimension)) paths[1:] = np.cumsum(step_length * steps, axis=0)return paths
The coefficient is the slope of against time, through the origin and divided by , and the prediction is a function too, so that the page and the tests compare with the same code.
def diffusion_coefficient(times: Array, msd: Array, dimension: int=2) ->float:"""The diffusion coefficient D (m²/s) from <r²> = 2 d D t: the slope of the least-squares line through the origin, divided by 2 d.""" t = np.asarray(times, dtype=np.float64) slope =float(np.sum(t * msd) / np.sum(t * t))return slope / (2.0* dimension)
def predicted_diffusion_coefficient( step_length: float, tau: float, dimension: int=2) ->float:"""D = l² / (2 d tau) (m²/s) for steps of length l every tau seconds: the steps are independent with mean square l², so <r²> = n l² after n steps, that is l² t / tau."""return step_length**2/ (2.0* dimension * tau)
The walkers take steps of 0.1 m every 0.5 s. Each run follows 500 walkers for 200 steps and records after every step; six runs with different seeds are made.
STEP =0.1# step length, mTAU =0.5# time between steps, sN_WALKERS =500N_STEPS =200SEEDS =range(6)times = TAU * np.arange(N_STEPS +1)predicted_d = predicted_diffusion_coefficient(STEP, TAU)
def msd_run(experiment: Experiment):"""Six runs of the walkers, their mean squared displacement, and the table of three times against the step rule.""" runs = [ random_walks(N_WALKERS, N_STEPS, STEP, np.random.default_rng(seed))for seed in SEEDS ] msds = np.array([mean_squared_displacement(paths) for paths in runs]) msd = msds.mean(axis=0) checkpoints = [N_STEPS //4, N_STEPS //2, N_STEPS] experiment.table("msd-run", [(n, times[n], msd[n], 4.0* predicted_d * times[n]) for n in checkpoints], [ ("steps", "", "d"), ("time", "s", ".1f"), ("measured <r²>", "m²", ".3f"), ("predicted 4 D t", "m²", ".3f"), ],"The mean squared displacement averaged over six runs of the walkers, at ""three times, against the prediction of the step rule.", )return runs, msds, msd
Figure 1: simulated The mean squared displacement of the walkers grows in a straight line with time, along the line the step rule predicts. The spread between the six runs is too small to see at this scale.
The table and the figure show one thing: the squared distance is proportional to the time, and the line is the one the step rule gives, with , not one fitted to the data. The data are simulated, and the check is the prediction of the model that made them.
The diffusion coefficient against the step rule
Each of the six runs gives its own coefficient from the slope of its own . The mean over the runs and its standard error are compared with the prediction.
def diffusion_check(experiment: Experiment, msds):"""The diffusion coefficient of each run, their mean, and its distance in standard errors from the step rule and from a wrong prediction; record the table of the runs.""" estimates = np.array([diffusion_coefficient(times, row) for row in msds]) mean_d, error_d = mean_and_error(estimates) off =abs(mean_d - predicted_d) / error_d wrong =abs(mean_d -2.0* predicted_d) / error_d experiment.table("diffusion-check",list(zip(SEEDS, estimates, strict=True)), [("seed", "", "d"), ("D", "m²/s", ".5f")],"The diffusion coefficient measured in each of the six runs.", )return mean_d, error_d, off, wrong
The diffusion coefficient measured in each of the six runs.
seed
D (m²/s)
0
0.00467
1
0.00467
2
0.00497
3
0.00475
4
0.00494
5
0.00498
The mean of the six measured coefficients, its standard error, and its distance in standard errors from the step rule and from a wrong prediction.
compared with
mean D (m²/s)
standard error (m²/s)
prediction (m²/s)
difference (standard errors)
step rule
0.00483
0.00006
0.00500
2.8
a coefficient twice as large
0.00483
0.00006
0.01000
84.2
Numerically verified (equal steps in the plane, six runs of five hundred walkers, four standard errors): The diffusion coefficient measured from the mean squared displacement agrees with within the tolerance of four standard errors, and a prediction twice as large is rejected. The measured coefficient is 0.00483 with a standard error of 0.00006 , 2.8 standard errors from the prediction of 0.00500 . The wrong prediction is 84 standard errors away, so the check can tell the two apart.
The Gaussian spread
The mean squared displacement tests the second moment only. The probability that a walker is within a radius of the start tests the shape. Take the radius that the prediction says contains half of the walkers after the last step, and count.
def radial_check(experiment: Experiment, runs) ->list:"""The fraction of walkers within three radii after the last step against the Gaussian prediction; record the table and return its rows.""" t_end = times[-1] radii = np.sqrt(4.0* predicted_d * t_end *-np.log(1.0- np.array([0.25, 0.5, 0.75])) ) rows = []for radius in radii: measured = np.array([fraction_within(paths[-1], radius) for paths in runs]) mean_f, error_f = mean_and_error(measured) predicted_f = predicted_fraction_within(radius, predicted_d, t_end) rows.append( (radius, mean_f, error_f, predicted_f, abs(mean_f - predicted_f) / error_f) ) experiment.table("radial-check", rows, [ ("radius", "m", ".2f"), ("measured fraction", "", ".3f"), ("standard error", "", ".3f"), ("predicted fraction", "", ".3f"), ("difference", "standard errors", ".1f"), ],"The fraction of walkers within three radii after the last step, over six ""runs, against the Gaussian prediction.", )return rows
The fraction of walkers within three radii after the last step, over six runs, against the Gaussian prediction.
radius (m)
measured fraction
standard error
predicted fraction
difference (standard errors)
0.76
0.257
0.007
0.250
1.0
1.18
0.510
0.006
0.500
1.7
1.67
0.764
0.008
0.750
1.7
Numerically verified (the last step of the walk, six runs, four standard errors): The fraction of walkers within a radius agrees with at three radii within four standard errors. The largest difference is 1.7 standard errors. The two checks use the same , so together they say that the walkers spread by the law and in the shape that the step rule predicts.
Limits
A model checked against a prediction, not against ink. The data are simulated from the rule that the prediction is derived from. The result says that the program implements the rule and that the derivation is right for it. It does not say that molecules take equal steps; no measurement is involved, so no claim here is experimentally supported.
What the tolerance can see. A coefficient wrong by more than four standard errors is caught, and a smaller error is not. Agreement of a second moment and three fractions is a test of the shape in four places, not a proof of it.
The plane. The numbers are for two dimensions. The formula is stated for one and two dimensions, and the unit tests check both; the lesson shows only the plane.
Only the long-time law. The linear growth is exact for equal independent steps at every step count, but the Gaussian shape is a limit of many steps.
No physics of the molecule. What sets and in a real liquid is not modelled. They are the two numbers the walk takes as given.
Self-check. The mean squared displacement of the walkers is a straight line in time. Does that show that every walker moves in a straight line at a fixed speed?
Yes: a straight line in means a constant speed.
No. A walker that moves at a constant speed has , so , which grows as the square of the time.
No: the line is in the average of the squared distance, and each walker’s distance grows only like the square root of time.
Right. The average of the square grows in proportion to because the cross terms between different steps cancel, so the typical distance grows like , slower than a straight line.
Solution to the self-check
The linear growth of is the signature of independent random steps, as the derivation shows. Motion at a constant velocity gives , and a walker held near a point gives a that stops growing. The exponent of time in tells these motions apart.
Exercises
Exercise 1.A doubled step. Run the walk with a step twice as long and the same time between steps. By what factor does the measured diffusion coefficient change, and what does the step rule predict?
Hint 1 of 1 for exercise 1
The coefficient depends on the step through its square.
Solution to exercise 1
The prediction is , so doubling multiplies it by four.
def doubled_step(experiment: Experiment, mean_d) ->None:"""Exercise 1: the diffusion coefficient of the doubled step in units of the first; record the table.""" doubled = [ random_walks(N_WALKERS, N_STEPS, 2.0* STEP, np.random.default_rng(seed))for seed in SEEDS ] doubled_d = np.array( [diffusion_coefficient(times, mean_squared_displacement(p)) for p in doubled] ) mean_2, error_2 = mean_and_error(doubled_d) experiment.table("doubled-step", [(mean_2 / mean_d, 4.0, error_2 / mean_d)], [ ("measured ratio", "", ".2f"), ("predicted ratio", "", ".1f"), ("standard error of the ratio", "", ".2f"), ],"The measured diffusion coefficient of the doubled step in units of the ""first, with the predicted ratio.", )
The experiment writes every figure of this page into results/, as <label>.png, and every number and table of the page, as the page shows it, into results/results.json. Its numbers match this page to the digits shown, and its figure files are the ones the page shows; digits beyond those can differ between machines, because floating-point libraries differ. Change the code of the experiment and run it again to see what changes.