Advanced Physical and Theoretical Chemistry: Molecular Dynamics
Whether you want to see molecules move on an atomistic level or want to investigate gas particles interacting with each other, or even follow stars or galaxies as they brush past each other, then the simulation of particles is the way to go. The mathematical and algorithmic foundations are surprisingly similar in all these cases.
In these two modules (one on fundamentals and one more in-depth on applications), we will write a particle simulation in two dimensions.
Whether it's planets or atoms you are considering, Newton's laws apply reasonably well, most importantly
which relates the force acting on a particle
The core idea of the Euler Integrator is to take the current (
Repeating this over and over with very small
A better alternative is the Verlet integrator. The idea is the same, but a slighly different formalism improves its accuracy over the Euler integrator. It is derived from considering two Taylor expansions of the trajectory forward and backwards in time:
Here
which is an update that does not require any velocity information.
Finally, the Velocity Verlet integrator yields explicit positions and velocities by following the Verlet integrator idea for the velocities rather than positions:
Here the trick is to obtain
Using this background, you will now implement Python code to calculate the trajectory of
You can develop and run code online without installation. Please submit your code with your report. It is recommended to read all tasks first before starting to develop code. Code examples given here will deliberately use beginner-level Python features to remain accessible, even at the cost of performance. Feel free to deviate from the suggested function naming and function signatures if you are familiar with Python. Implementations using NumPy/JAX/numba are accepted as well, provided they solve the same problem.
Part 1: Fundamentals
- First, please familiarise yourself with the basics of Python, e.g. via the self-learning course in moodle or via the material for the block course in Python which contains all you need. Knowing NumPy or JAX is advantageous but not necessary to solve the tasks.
- Let's create a single particle. Write a function
init_universe(bounds: tuple[float, float]) -> tuple[tuple[float, float], tuple[float, float]]
which randomly places a particle in the box defined by the tuplebounds
and returns its position and a random velocity. The box is rectangular and goes from the origin to the pointbounds
. -
Now the particle shall move. Write a function
get_trajectory(total_time: float, time_step: float, bounds) -> list[tuple[float, float]]
which uses the Euler integrator andinit_universe
to calculate the trajectory of a single particle by iteration over small steps (given astime_step
) until the total time (total_time
) is elapsed. This function returns a list of coordinates where the particle has been. You can visualise your trajectory usingbounds = (10, 10) trajectory = get_trajectory(10, 0.01, bounds) import matplotlib.pyplot as plt plt.plot([_[0] for _ in trajectory], [_[1] for _ in trajectory]) plt.plot((0, 0, bounds[0], bounds[0], 0), (0, bounds[1], bounds[1], 0, 0), color="red")
-
So far the particle starts in the box and leaves it quickly. That should not happen. Now constrain your particle to the box by writing a new function
enforce_boundaries(position: tuple[float, float], velocity: tuple[float, float], bounds: tuple[float, float]) -> tuple[tuple[float, float], tuple[float, float]]
which makes sure the particle bounces off the box walls. You should assume that only one box boundary is crossed between time steps. Add this function toget_trajectory
and verify that the particle stays in the box. - What happens if you increase the time step? Explore a wide range of time steps and document your findings by showing the trajectories. Explain your findings and discuss what they mean for simulating single particles in a box.
- Describe what would be needed to be done if we had an irregular boundary, i.e. not a simple rectangle? Think of a circle or a polygon. No implementation is needed, just a description of how to deal with the reflection in that case.
- Which sources of deviations from the true trajectory do you expect in this particular case? Is the Euler integrator sufficient for this problem?
Part 2: Application
Now generalize your code to multiple particles using NumPy. We will consider charged particles avoiding each other in the box, i.e. all charges are positive.
-
First write a function to calculate the Coulomb force acting on a set of charged particles:
coulomb_repulsion_force(charges: np.ndarray, positions: np.ndarray) -> np.ndarray
. You may use atomic units, i.e. the prefactor in the force expression is 1. The force for the -th particle is -
Write a function
coulomb_energy(charges, positions)
which calculates the Coulomb energy from the pairwise distances : -
Update the universe creation to generate
particles of random positions, velocities, masses and charges (which are the return values):init_charged_universe(n: int, bounds: tuple[float, float]) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]
. -
Implement the Euler integrator for this problem, writing
get_euler_trajectory(n_particles: int, total_time: int, time_step: float, bounds: tuple[float, float]) -> tuple[np.ndarray, np.ndarray]
which returns the trajectory and the total energy. This includes the kinetic energy: -
Write
get_velocity_verlet_trajectory
with the same function arguments asget_euler_trajectory
to use velocity verlet instead. -
Choose a number of particles between two and five and run both integrators such that the particles interacted with each other/the box at least a few times. Running the integrators should take roughly 10 seconds. Suggest a metric for the integration error which should be derived from the total energy data. Make a table with the integration error as a function of time step for the Euler integrator and the Velocity Verlet integrator and discuss your findings. (If the metric does not improve with smaller time steps, then either the integrator is faulty or the metric is flawed.)
-
Suggest a way how to implement the direction reversal upon touching the box in the Verlet algorithm where no velocities are available. No actual implementation needed.