Skip to content

MigrationGuide

Andrey Prokopenko edited this page Jun 9, 2025 · 3 revisions

ArborX

APIv1 to APIv2 migration guide

APIv2 (used in ArborX 2.x) extensively changed the spatial index API, breaking backwards compatibility. This guide provides motivation behind the change and general tips for migrating an existing APIv1 (ArborX 1.x) code to APIv2.

Motivation

APIv1 had multiple severe limitations:

  • Dimensionality was hardcoded to 3D

    APIv1 only supported 3D objects. 2D objects were treated as 3D through setting $z = 0$, at the cost of extra memory storage and slower traversal.

  • User-provided data was restricted to points and boxes

    Spatial indexes lacked the capability of storing general user data, including non-trivial geometries (e.g., triangles, tetrahedrons, etc) and associated metadata (e.g., global index corresponding to a geometry).

  • Only coarse search was supported

    As APIv1 was only able to store user data as points and boxes, the queries were only able to perform coarse search.

    For spatial queries, that meant that a user had to provide a callback where the fine search (intersection with user geometries) was performed.

    For nearest queries, this meant that ArborX only considered the distances to stored boxes, and not to the user data. This meant that for user geometries other than points and boxes, the results produced by ArborX could be wrong.

  • Callbacks were limited

    Signatures of callbacks only took integer as the second argument, requiring user to store more data inside the callback.

Migration tips

Geometries

In APIv1, ArborX::Point and ArborX::Box were hardcoded to single-precision 3D data. In contrast, all geometries in APIv2 are templated on both the dimension and the stored type:

template<int DIM, typename Coordinate = float>
struct Geometry;

Some geometries, for example KDOP, may have additional template arguments.

AccessTraits

APIv2 removed the second template argument in AccessTraits, which in APIv1 had to be ArborX::PrimitivesTag or ArborX::PredicatesTag.

In most situations, simply removing this argument in the user code is sufficient. One exception is when the code provided AccessTraits specialization for the same data structure for both PrimitivesTag and PredicatesTag. In this case, the simplest approach would be to introduce user space tags and template user data structure on those.

For example,

struct PrimitivesTag{};
struct PredicatesTag{};

template<typename Tag, typename... Args>
struct UserData {...};

template<typename... Args>
struct ArborX::AccessTraits<UserData<PrimitivesTag, Args...>> {...};

template<typename... Args>
struct ArborX::AccessTraits<UserData<PredicatesTag, Args...>> {...};

APIv2 in APIv1 mode

APIv1 used int as the default returned value or as the second argument to the callback. To reproduce this behavior, construct an index with attached indexes and use a callback to extract the index:

struct ExtractIndex
{
  template <typename Query, typename Value, typename Output>
  KOKKOS_FUNCTION void operator()(Query const &, Value const &value,
                                  Output const &out) const
  {
    out(value.index);
  }
};

Primitives primitives;
using Value = ...; // return type of ArborX::AccessTraits<Primitives>::get()

// The template arguments could be omitted (to make use of deduction guides)
BoundingVolumeHierarchy<MemorySpace, ArborX::PairValueIndex<Value,int>>
  bvh(space, ArborX::Experimental::attach_indices(primitives));
Kokkos::View<int*> offsets("offsets", 0);
Kokkos::View<int*> indices("indices", 0);
bvh.query(space, predicates, ExtractIndex{}, indices, offsets);

ARBORX_VERSION

If one desires to be able to build code against both versions of ArborX, ARBORX_VERSION macro can be used to distinguish whether the code is built against APIv1 (ARBORX_VERSION < 20000) or APIv2 (ARBORX_VERSION >= 20000).

Clone this wiki locally