Αρχή

Αλγόριθμος Velocity Verlet

Ο αλγόριθμος Velocity Verlet χρησιμοποιείται στη Μοριακή Δυναμική για την αριθμητική επίλυση των εξισώσεων κίνησης για τον υπολογισμό θέσεων και ταχυτήτων.

$$ v\left(t+\frac{1}{2}\Delta t\right) = v(t) + \frac{1}{2}\Delta t \, a(t) $$
$$ r(t+\Delta t) = r(t) + \Delta t \, v\left(t+\frac{1}{2}\Delta t\right) $$
$$ v(t+\Delta t) = v\left(t+\frac{1}{2}\Delta t\right) + \frac{1}{2}\Delta t \, a(t+\Delta t) $$
Allen & Tildesley (2017)

Η διαδικασία γίνεται σε τρία βήματα: πρώτα υπολογίζεται η ταχύτητα στο μισό χρονικό βήμα, στη συνέχεια ενημερώνονται οι θέσεις και τέλος ολοκληρώνεται η ενημέρωση των ταχυτήτων χρησιμοποιώντας τις νέες επιταχύνσεις.

Ο παρακάτω κώδικας προσομοιώνει την κίνηση δύο σωματιδίων που αλληλεπιδρούν μέσω της δύναμης Lennard–Jones χρησιμοποιώντας τον αλγόριθμο Velocity Verlet. Σε κάθε χρονικό βήμα έχουμε:

  1. Υπολογισμό των δυνάμεων μεταξύ των σωματιδίων με βάση το δυναμικό Lennard–Jones.
  2. Ενημέρωση της ταχύτητας κατά μισό χρονικό βήμα (half-step velocity).
  3. Ενημέρωση των θέσεων των σωματιδίων χρησιμοποιώντας τις ενδιάμεσες ταχύτητες.
  4. Επαναϋπολογισμό των δυνάμεων στις νέες θέσεις.
  5. Ολοκλήρωση της ενημέρωσης των ταχυτήτων με τις νέες επιταχύνσεις.
  6. Γραφική απεικόνιση της θέσης των σωματιδίων για την παρακολούθηση της κίνησης.
import numpy as np
import matplotlib.pyplot as plt

# ==============================
# ΠΑΡΑΜΕΤΡΟΙ ΠΡΟΣΟΜΟΙΩΣΗΣ
# ==============================

dt = 0.01      # χρονικό βήμα Δt 
steps = 200    # αριθμός βημάτων, μπορεί να αλλάξει για μικρότερη ή μεγαλύτερη εξομοίωση
m = 1.0        # μάζα σωματιδίων

# Παράμετροι δυναμικού Lennard–Jones
epsilon = 1.0  # βάθος δυναμικού 
sigma = 1.0    # χαρακτηριστική απόσταση (όπου V=0)

# ==============================
# ΑΡΧΙΚΕΣ ΣΥΝΘΗΚΕΣ
# ==============================

# αρχικές Θέσεις δύο σωματιδίων (σε 2D)
r = np.array([
    [-1.5, 0.0],
    [ 1.5, 0.0]
])

# αρχικές ταχύτητες δύο σωματιδίων
v = np.array([
    [0.5, 0.2],
    [-0.5, -0.2]
])

# ==============================
# ΔΥΝΑΜΗ LENNARD-JONES
# ==============================

def lennard_jones_forces(r):
    """
    Υπολογίζει τις δυνάμεις μεταξύ όλων των ζευγών σωματιδίων.
    
    Επιστρέφει πίνακα forces ίδιου σχήματος με το r.
    """
    N = len(r)                    # αριθμός σωματιδίων
    forces = np.zeros_like(r)     # αρχικοποίηση δυνάμεων

    # Υπολογίζουμε μόνο για i < j (κάθε ζεύγος μία φορά)
    for i in range(N):
        for j in range(i+1, N):

            # διάνυσμα απόστασης r_i - r_j
            rij = r[i] - r[j]

            # μέτρο απόστασης |r| σωματιδίων
            dist = np.linalg.norm(rij)

            # αποφυγή διαίρεσης με το μηδέν
            if dist == 0:
                continue

            # υπολογισμός όρων (σ/r)^6 και (σ/r)^12
            sr6 = (sigma / dist)**6
            sr12 = sr6**2

            # μέτρο δύναμης (scalar)
            force_mag = 24 * epsilon * (2*sr12 - sr6) / dist**2

            # διάνυσμα δύναμης
            fij = force_mag * rij

            # δράση-αντίδραση 
            forces[i] += fij
            forces[j] -= fij

    return forces

# ==============================
# ΑΡΧΙΚΗ ΕΠΙΤΑΧΥΝΣΗ
# ==============================

# a = F/m
a = lennard_jones_forces(r) / m

# ==============================
# ΑΠΟΘΗΚΕΥΣΗ ΤΡΟΧΙΑΣ
# ==============================

# λίστα που κρατά όλες τις προηγούμενες θέσεις (για τα plots)
traj = [[] for _ in range(len(r))]

# ==============================
# ΕΝΕΡΓΟΠΟΙΗΣΗ INTERACTIVE PLOT
# ==============================

plt.ion()  # επιτρέπει live animation

# ==============================
# ΚΥΡΙΟ LOOP ΠΡΟΣΟΜΟΙΩΣΗΣ
# ==============================

for step in range(steps):

    # ---------------------------------
    # ΒΗΜΑ 1: μισό βήμα στην ταχύτητα
    # v(t + dt/2) = v(t) + 1/2 a(t) dt
    # ---------------------------------
    v_half = v + 0.5 * a * dt

    # ---------------------------------
    # ΒΗΜΑ 2: ενημέρωση θέσης
    # r(t + dt) = r(t) + v_half * dt
    # ---------------------------------
    r = r + v_half * dt

    # ---------------------------------
    # ΒΗΜΑ 3: νέες δυνάμεις -> νέα επιτάχυνση
    # ---------------------------------
    a_new = lennard_jones_forces(r) / m

    # ---------------------------------
    # ΒΗΜΑ 4: ολοκλήρωση ταχύτητας
    # v(t + dt) = v_half + 1/2 a(t+dt) dt
    # ---------------------------------
    v = v_half + 0.5 * a_new * dt

    # ενημέρωση επιτάχυνσης για το επόμενο βήμα
    a = a_new

    # ---------------------------------
    # ΑΠΟΘΗΚΕΥΣΗ ΤΡΟΧΙΑΣ
    # ---------------------------------
    for i in range(len(r)):
        traj[i].append(r[i].copy())

    # ==============================
    # PLOT
    # ==============================

    plt.clf()  # καθαρίζει το προηγούμενο frame

    # σχεδίαση τροχιών 
    for i in range(len(r)):
        t = np.array(traj[i])
        plt.plot(t[:,0], t[:,1], label=f"particle {i}")

    # σχεδίαση σωματιδίων 
    for i in range(len(r)):
        circle = plt.Circle((r[i,0], r[i,1]), 0.2)
        plt.gca().add_patch(circle)

    # όρια άξονων
    plt.xlim(-5, 5)
    plt.ylim(-5, 5)

    # σωστή αναλογία αξόνων (ίδια κλίμακα x και y)
    plt.gca().set_aspect('equal')

    plt.title(f"Step {step}")
    plt.grid()

    # μικρή παύση για το animation
    plt.pause(0.01)


plt.ioff()
plt.show()

Βιβλιογραφία

Allen, M. P., & Tildesley, D. J. (2017). Computer simulation of liquids (2nd ed.). Oxford University Press.

Atkins, P. W. (1998). Physical chemistry (6th ed.). Oxford University Press.

Bortoleto, E., Prados, E., Seriacopi, V., Fukumasu, N., Lima, L., Machado, I., & Souza, R. (2016). Numerical modeling of adhesion and adhesive failure during unidirectional contact between metallic surfaces. Friction, 4, 1–12. https://doi.org/10.1007/s40544-016-0119-5

Schwerdtfeger, P., & Wales, D. J. (2024). 100 years of the Lennard-Jones potential. Journal of Chemical Theory and Computation, 20(9), 3379–3405. https://doi.org/10.1021/acs.jctc.4c00135