Neighbor lists

A periodic cutoff list is linkcell pairs_within: one row per atom-image, with the caller’s shift. An open axis uses vesin, which accepts per-axis periodicity. knearest is the k-nearest list.

Layers

Layer

API

Backend

Python server geometry

eon.geometry.neighbors.neighbor_list

linkcell.pairs_within when every axis is periodic; vesin when one is open

Process-atom shells / displace

eon.atoms / get_process_atoms

same

Classical C++ pair pots (LJ, Morse, LJCluster, ZBL)

rgpot::nlist::PairListCache (Verlet-skin cache over vesin, in rgpot)

vesin_neighbors (C)

Metatomic

pot-local call into vesin C

same libvesin / vendored TU

External engines (LAMMPS, VASP, ASE, …)

engine-owned

engine NL

Fortran pots (SW, EDIP, Lenosky, EAM-Al, CuH2, FeHe, Tersoff, TIP4P-H)

rgpot_neighbors, CSR full-list table in rgpot

vesin_fortran, vendored in rgpot

Python

from eon.geometry import neighbor_list, neighbor_list_linkcell, neighbor_list_pairs, pbc
nl = neighbor_list(structure, cutoff=4.0)
i, j, S = neighbor_list_pairs(structure, cutoff=4.0)

brute=True exists for API compatibility. On a fully periodic cell the list is linkcell.pairs_within. neighbor_list unique-indexes atoms (historical eOn adjacency). neighbor_list_pairs keeps every atom-image row (i, j, cell shift S). Displacement is r[j] - r[i] + S @ box. A squared distance on a linkcell row is strictly below the cutoff squared.

minimage

eon.geometry.pbc applies the minimum-image wrap. When minimage is installed it uses minimage.Cell.from_vesin (the same wrap linkcell uses) and Cell.wrap_many for packed (N, 3) rows. Without that extra the numpy path (pbc_eon_legacy) runs.

from eon.geometry import pbc
dr = pbc(r_j - r_i, box)

linkcell

neighbor_list_linkcell is the same periodic cutoff adjacency as neighbor_list, built only from pairs_within. linkcell.knearest is a separate call for the k nearest atoms.

from eon.geometry import neighbor_list_linkcell
pairs = neighbor_list_linkcell(structure, cutoff=4.0)

Install extras: pip install minimage linkcell. Tests skip when those packages are missing.

C++

Classical pair pots (LJ, LJCluster, Morse, ZBL) live in rgpot and reach vesin through rgpot::nlist::PairListCache. eOn consumes them through RgpotAdapter; the cache API lives in rgpot. See the rgpot docs for Options, evaluate, and ensureVisit.

The candidate list is built at cutoff + skin. While every atom stays within skin/2 of its build position, the cache reuses pairs and derives exact vectors from current positions, filtered at the true cutoff. Pair content matches a fresh build. The pool matches slots by geometry proximity, so several NEB images that share one pot instance each keep a live list under any thread-to-image assignment, including NEB’s per-iteration worker threads. That assignment needs a proximity-matched pool; thread_local storage fails under worker reassignment.

eonc::VesinNeighbors (vesin convention r_ij = r_j − r_i + S·H) is the RAII wrapper for consumers that need vesin’s own buffers:

#include "eon/VesinNeighbors.h"
eonc::VesinNeighbors nl;
eonc::VesinNeighbors::Options opt;
opt.cutoff = 5.0;
nl.compute(R, nAtoms, box9, opt);

eoncbase always links vesin. Resolution order: an installed vesin (pkg-config, cmake, or plain library); else the copy the rgpot subproject already builds; else eOn’s own vendored translation unit. When rgpot comes from the wrap, reusing its vesin avoids two definitions of every vesin symbol in one link.

Reuse the same VesinNeighborList across compute calls so allocations recycle. Free (or destroy a stack-local list) only when finished.

ASV benchmarks

Wall-clock timing for client changes goes through the ASV suite under benchmarks/.

ASV class

Fixture

vesin pot path

TimePointMorsePt

data/point_morse_pt

Morse force pairs

TimeMinimizationLJCluster

data/min_lj_cluster

LJCluster force pairs (many force evals)

TimeSaddleSearchMorseDimer

data/one_pt_saddle_search

Morse + saddle path

TimeNEBMorsePt

data/neb_morse_pt

Morse band forces

CI

  1. PR: the Benchmark PR workflow builds eonclient at the base SHA and the head SHA with the PR’s benchmarks/ tree, runs asv run --set-commit-hash … --quick, then asv-perch comments the main→PR comparison.

  2. main: the ASV dashboard workflow restores the orphan branch asv-results, runs history, and publishes with asv-tachyon to eondocs.org/bench and bench.eondocs.org.

Local

Same sequence as benchmarks/README.md:

pixi run meson setup bbdir --prefix=$CONDA_PREFIX --libdir=lib --buildtype release
pixi run meson install -C bbdir
pixi run bash -c 'pip install asv asv-tachyon'
pixi run asv machine --yes
pixi run asv run -E "existing:$(which python)" --quick

Local pair comparison: build and install both clients (or install sequentially) and run the same ASV class names. Review still uses the PR workflow.

Fortran

Fortran kernels and the vendored vesin Fortran interface both live in rgpot. Every kernel reaches vesin the same way: rgpot_neighbors builds a CSR full neighbour list with pair vectors and distances (Verlet skin on the C API), and the kernels read rows out of it. The per-pot CSR helper modules that used to sit beside the fixed-form kernels here (vesin_al, vesin_cuh2, vesin_fehe) moved out with those kernels.

Fortran pots take the box diagonal only (orthorhombic). Porting rules and the neighbour-table cell convention are in docs/orgmode/reference/fortran_potentials.org in rgpot.