C ABI

August 20, 2026 ยท View on GitHub

Header: include/linkcell.h. Prefix lc_. Caller owns every buffer. C++ (include/linkcell.hpp) is a header-only wrap of these entry points. Device searches are lc_gpu_* in include/linkcell_gpu.h; link the package's linkcell-gpu target.

Cell

typedef struct lc_cell {
  double ax, ay, az;
  double bx, by, bz;
  double cx, cy, cz;
  double ox, oy, oz;
} lc_cell;

lc_cell lc_cell_ortho(double lx, double ly, double lz);

Lattice vectors are rows a, b, c (the same order vesin uses) plus an origin. lc_cell_ortho builds a diagonal box at the origin.

int lc_knearest(const double *xyz, size_t n, const lc_cell *simbox,
                size_t k, const int *mask, double cell_hint,
                int *out_nn);

int lc_knearest_d2(const double *xyz, size_t n, const lc_cell *simbox,
                   size_t k, const int *mask, double cell_hint,
                   int *out_nn, double *out_d2);

int lc_knearest_many(const double *xyz, size_t n, size_t n_frames,
                     const lc_cell *simbox, size_t k, const int *mask,
                     double cell_hint, int *out_nn, double *out_d2);
ArgumentContract
xyzn packed x y z triples. Not null.
nPoint count. size_t. Zero is an error.
simboxPeriodic parallelepiped. Not null.
kNeighbours per source. size_t. Zero is an error.
maskNULL (keep every point) or n ints, nonzero to include. A zero drops the point as both source and candidate.
cell_hintTarget bin edge. <= 0 selects the default (3.0 in box units).
out_nnCaller-owned, length n * k. Unused slots are -1. Neighbours of source i are out_nn[i*k + t], nearest first.
out_d2Optional. Caller-owned, length n * k (n_frames * n * k for lc_knearest_many). Unused slots are NaN. NULL skips distances.
n_framesFrame count for lc_knearest_many. xyz is frame-major.

lc_knearest is the one-frame, indices-only entry. Returns 0 on success, nonzero on failure.

Errors and version

const char *lc_last_error(void);
const char *lc_version(void);

Both return pointers the caller must not free. lc_last_error is thread-local and NULL after a successful lc_knearest on that thread, or if none has failed yet. lc_version does not read or write the slot. The pointer is invalid after the next lc_knearest on the same thread. Distinct searches may run concurrently; each thread reads its own slot. lc_version is process-static.

C++ wrap

namespace linkcell {
class Error : public std::runtime_error { /* status() is the C return */ };

struct Cell {
  std::array<double, 3> a, b, c, origin;
  static Cell ortho(double lx, double ly, double lz);
  static Cell from_vectors(std::array<double, 3> a, std::array<double, 3> b,
                           std::array<double, 3> c,
                           std::array<double, 3> origin = {});
  lc_cell raw() const;
};

class Neighbours {
  int neighbour(std::size_t i, std::size_t j) const; /* or -1 */
  double dist2(std::size_t i, std::size_t j) const;  /* unused NaN */
  std::size_t n() const;
  std::size_t k() const;
  const int *data() const;
  const double *distances() const;
};

void knearest_into(const double *xyz, std::size_t n, const Cell &cell,
                   std::size_t k, int *out, std::size_t out_len,
                   const int *mask = nullptr, double cell_hint = 0.0);

Neighbours knearest(const double *xyz, std::size_t n, const Cell &cell,
                    std::size_t k, const int *mask = nullptr,
                    double cell_hint = 0.0);

const char *version();
}

knearest returns an owning packed Neighbours (n * k ints). knearest_into writes a caller buffer and requires out_len == n * k. Neither returns std::vector<std::vector<int>>. Failure throws linkcell::Error. Requires C++17.

Rust errors (same crate)

linkcell::Error:

VariantMeaning
ZeroKk == 0
BadBoxA box length is not strictly positive, or H is singular
EmptyThe point list is empty
BufferSizeknearest_into / out length is not n * k
MaskLenmask is Some and mask.len() != n
TooManyCellslinked-cell mesh overflows the bin cap
Overflown * k does not fit a slice

Error::Empty is not a wrong-length buffer.

Walk and stop rule: algorithm.