jaxdem.colliders.naive#

Naive \(O(N^2)\) collider implementation.

Classes

NaiveSimulator(*, overflow)

Implementation that computes forces and potential energies using a naive \(O(N^2)\) all-pairs loop.

class jaxdem.colliders.naive.NaiveSimulator(*, overflow: Array = <factory>)#

Bases: Collider

Implementation that computes forces and potential energies using a naive \(O(N^2)\) all-pairs loop.

This collider evaluates interactions between all particle pairs directly, without any spatial partitioning or binning.

The total force acting on particle \(i\) is the direct sum of its interactions with all other particles \(j\) in the system:

\[\mathbf{F}_i = \sum_{j=0}^{N-1} \mathbf{F}_{ij}(\mathbf{x}_i, \mathbf{x}_j, r_i, r_j) \cdot M_{ij}\]

where \(\mathbf{F}_{ij}\) is the force vector computed by the physical force model, and \(M_{ij}\) is the interaction eligibility mask. The mask accounts for:

  • Clump member exclusions (internal clump particles do not exert forces on each other)

  • Bond connectivity exclusions

  • Contact overlap/cutoff checks

Runtime and Cost Analysis#

This collider always evaluates a fixed number of pair checks:

\[\text{cost} \approx N^2 \cdot C_{interaction}\]

where \(C_{interaction}\) is the cost of a single pairwise force/energy query.

Because the algorithm does not partition space into cells or project coordinates onto axes, its execution time is completely independent of:

  • The spatial distribution or packing fraction \(\phi\) of the system

  • The particle polydispersity \(\alpha\)

  • Performance Trade-off:

    • For small systems (:math:`N le 10^3 - 2 cdot 10^3` depending on the GPU): NaiveSimulator is often the fastest collider because it does no sorting, hashing, or bookkeeping. This gives full GPU thread utilization and short JIT compilation times.

    • For large systems (:math:`N ge 10^4`): The quadratic complexity \(O(N^2)\) becomes a severe performance bottleneck, and spatial partitioning colliders are significantly faster.

Complexity#

  • Time: \(O(N^2)\).

  • Memory: \(O(N)\) (the collider stores no neighbor tables or grid structures).

static compute_potential_energy(state: State, system: System) tuple[State, System, jax.Array][source]#

Compute the total potential energy of the system with a naive \(O(N^2)\) all-pairs loop.

This method iterates over all particle pairs (i, j) and sums the potential energy contributions of the system.force_model.

Parameters:
  • state (State) – The current state of the simulation.

  • system (System) – The configuration of the simulation.

Returns:

Tuple of (state, system, energy).

Return type:

Tuple[State, System, jax.Array]

static create_neighbor_list(state: State, system: System, cutoff: float, max_neighbors: int) tuple[State, System, jax.Array, jax.Array][source]#

Compute a neighbor list with a naive \(O(N^2)\) all-pairs search.

Parameters:
  • state (State) – The current state of the simulation.

  • system (System) – The configuration of the simulation.

  • cutoff (float) – The interaction radius (force cutoff).

  • max_neighbors (int) – Maximum number of neighbors to store per particle.

Returns:

A tuple containing: - state: The simulation state. - system: The simulation system. - neighbor_list: Array of shape (N, max_neighbors) containing neighbor indices. - overflow: Boolean flag. True when any particle has more than

max_neighbors neighbors.

Return type:

Tuple[State, System, jax.Array, jax.Array]

static compute_force(state: State, system: System) tuple[State, System][source]#

Compute the total force acting on each particle with a naive \(O(N^2)\) all-pairs loop.

This method sums the force contributions of the system.force_model over all particle pairs (i, j) and updates the particle forces.

Parameters:
  • state (State) – The current state of the simulation.

  • system (System) – The configuration of the simulation.

Returns:

A tuple containing the updated State object with computed forces and the unmodified System object.

Return type:

Tuple[State, System]