jaxdem.colliders.naive#
Naive \(O(N^2)\) collider implementation.
Classes
|
Implementation that computes forces and potential energies using a naive \(O(N^2)\) all-pairs loop. |
- class jaxdem.colliders.naive.NaiveSimulator(*, overflow: Array = <factory>, history: Array = <factory>)#
Bases:
ColliderImplementation 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.
Pair History#
The collider stores force-model history densely:
history[i, j]is the stored history of the directed pair(i, j), the interaction onifromj. Every pair keeps the same slot for the life of the collider, so no remapping is ever needed. Stateless laws have a zero-width trailing axis, which costs nothing.System.createallocates the buffer.Complexity#
Time: \(O(N^2)\).
Memory: \(O(N^2 H)\) for a law with history width \(H\), otherwise \(O(N)\).
- history: Array#
Dense directed pair history with force-model-defined trailing dimensions.
- history_pair_shape(state_shape: tuple[int, ...]) tuple[int, ...][source]#
Leading shape of the stored pair history for
state_shape.Nonemeans this collider has no persistent pair memory, so it can only run stateless force laws.
- refresh(state: State, force_model: Any | None = None, *, reset_history: bool = False) Self[source]#
Reallocate the dense history when the particle count changes.
- reindex_history(state: State, system: System, new_to_old: jax.Array) System[source]#
Gather the dense history rows and columns of the surviving particles.
- property metadata: dict[str, Any][source]#
The naive collider has no construction parameters to persist.
- static get_history(state: State, system: System, neighbor_list: jax.Array) jax.Array[source]#
Gather stored history for an explicit
(N, k)pair query.
- 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, using the stored pair history.
- 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:
- 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_neighborsneighbors.
- Return type:
- static compute_force(state: State, system: System, *, advance_history: bool = True, quasistatic: bool = False) 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_modelover all particle pairs (i, j) and updates the particle forces. Withadvance_historythe dense pair history is advanced in place.- Parameters:
- Returns:
The updated
Statewith computed forces and theSystemwith the (possibly advanced) pair history.- Return type: