Source code for swarmsim.Interactions.lennard_jones

import numpy as np
from swarmsim.Interactions import Interaction
from swarmsim.Utils import compute_distances
from swarmsim.Populations import Population
from typing import Optional


[docs] class LennardJones(Interaction): """ Lennard-Jones interaction potential for agent-agent interactions. This interaction implements the classic Lennard-Jones potential, which models both attractive and repulsive forces between agents. The potential combines a strong short-range repulsion (r^-12 term) with a weaker long-range attraction (r^-6 term), commonly used to model molecular interactions and agent clustering. The interaction force deriving from a Lennard-Jones potential is: .. math:: F(r) = 24\\epsilon \\frac{1}{r^2} \\left[ 2\\left(\\frac{\\sigma}{r}\\right)^{12} - \\left(\\frac{\\sigma}{r}\\right)^6 \\right] where: - :math:`\\epsilon` is the depth of the potential well (energy scale) - :math:`\\sigma` is the distance at which the potential is zero (length scale) - :math:`r` is the distance between agents Parameters ---------- target_population : Population The population that receives Lennard-Jones forces. source_population : Population The population that generates Lennard-Jones forces. config_path : str Path to the YAML configuration file containing interaction parameters. name : str, optional Name identifier for the interaction. Defaults to class name if None. Attributes ---------- target_population : Population Population affected by Lennard-Jones forces. source_population : Population Population generating Lennard-Jones forces. epsilon : np.ndarray or None Energy scale parameter(s), shape depends on parameter configuration. sigma : np.ndarray or None Length scale parameter(s), shape depends on parameter configuration. params_shapes : dict Defines expected shapes for interaction parameters. Config Requirements ------------------- The YAML configuration file must contain the following parameters under the interaction's section: parameters : dict Parameter configuration for the Lennard-Jones interaction. Typical structure: - ``mode`` : str - Parameter assignment mode (e.g., "Random", "Fixed") - ``names`` : list - List of parameter names ["epsilon", "sigma"] - ``limits`` or ``values`` : dict - Parameter ranges or fixed values Notes ----- **Force Characteristics:** - **Equilibrium Distance**: Force is zero at :math:`r = 2^{1/6}\\sigma \\approx 1.122\\sigma` - **Attractive Region**: :math:`r > 2^{1/6}\\sigma` (negative force, particles attract) - **Repulsive Region**: :math:`r < 2^{1/6}\\sigma` (positive force, particles repel) - **Long-Range**: Force decays as :math:`r^{-7}` for large distances Raises ------ FileNotFoundError If the configuration file is not found. KeyError If required interaction parameters are missing in the configuration file. ValueError If parameter shapes are incompatible with population sizes. """ def __init__(self, target_population: Population, source_population: Population, config_path: str, name: str = None) -> None: super().__init__(target_population, source_population, config_path, name) self.epsilon: Optional[np.ndarray] = None self.sigma: Optional[np.ndarray] = None self.params_shapes = { 'epsilon': (), 'sigma': () }
[docs] def reset(self): super().reset() self.epsilon = self.params.get('epsilon') self.sigma = self.params.get('sigma')
[docs] def get_interaction(self): """ Compute Lennard-Jones interaction forces between source and target populations. This method calculates the attractive and repulsive forces between agents according to the Lennard-Jones potential. The computation handles both the attractive long-range and repulsive short-range components of the interaction. Returns ------- np.ndarray Lennard-Jones forces array of shape ``(N_target, 2)`` where: - ``N_target`` is the number of agents in the target population - Each row represents the total LJ force on one target agent in 2D space Force vectors point away from source agents for repulsion, toward them for attraction. Notes ----- **Algorithm Steps:** 1. **Distance Computation**: Calculate pairwise distances between all source-target pairs 2. **Force Magnitude**: Evaluate Lennard-Jones force equation for each pair 3. **Force Direction**: Compute unit vectors for force direction 4. **Vector Forces**: Combine magnitude and direction for vector forces 5. **Force Summation**: Sum contributions from all source agents """ # Compute pairwise distances and relative positions between agents distances, relative_positions = compute_distances(self.target_population.x[:, :2], self.source_population.x[:, :2]) # Prevent division by zero distances = np.maximum(distances, 1e-6) # Get epsilon and sigma values with correct shape epsilon = self.epsilon.reshape(-1, 1) # shape: (N_target, 1) sigma = self.sigma.reshape(-1, 1) # shape: (N_target, 1) # Compute normalized sigma/distance sigma_over_r = sigma / distances # Compute Lennard-Jones scalar force magnitude lj_scalar = 24 * epsilon * (2 * (sigma_over_r ** 12) - (sigma_over_r ** 6)) / (distances ** 2) # Compute vector force forces = np.sum(lj_scalar[:, :, np.newaxis] * relative_positions, axis=1) return forces